70-562:Deploying Web applications

Artykuł pochodzi w serii przygotowań do egzaminu 70-562 ASP.NET.

W dzisiejszej lekcji powiemy sobie o wdrażaniu aplikacji internetowych w ASP.NET.

Tworzenie projektu Web Setup

Web setup jest narzędziem, które wspomaga wdrażanie naszej aplikacji internetowej. Projekt Web Setup jest bardzo podobny do standardowego projektu Setup, który mamy w aplikacjach Windows Forms, lecz dostarcza specjalnych właściwości wymaganych przez aplikację Web. Aby stworzyć nowy projekt typu Web Setup musimy podczas tworzenia projektu wybrać zakładkę Other Project Types –> Setup  and Deployment –> Web Setup Project.
image
Po stworzeniu nowego projektu, Visual dołączy projekt do solution oraz wyświetli edytor “File System”. Następnie musimy dodać do nowo utworzonego projektu nowy element. Ustawiamy się na projekcie i wybieramy Add-> Project Output. W oknie tworzenia Project Output wybieramy Content i configuration ustawiamy na Active. W tym momencie zapewniliśmy sobie dostęp do naszego projektu, który chcemy wdrażać.

Tworzenie warunków uruchomienia

Dzięki warunkom uruchomienia możemy sobie sprawdzać np. czy na serwerze znajdują się Service Packi albo inne komponenty, które są wymagane do wdrożenia naszej aplikacji. Do tworzenia i zarządzania warunkami uruchomienia używa się edytora Launch Condition. Aby z niego skorzystać klikamy prawym przyciskiem na projekcie, następnie wybieramy View-> Launch Condition. Teraz aby dodać nowy warunek klikamy prawym przyciskiem na folderze Launch Condition i wybieramy Add Launch Condition:
image

 

Istnieją dwie główne gałęzie w edytorze: Search Target Machine oraz Launch Condition:
Search Target Machine – pozwala na zdefiniowanie kryteriów wyszukiwania przed instalacją. Domyślnie zawiera wyszukiwanie dla IIS ale możemy dodać do niego pliki rejestru, Windows Installer Search Condition itp.
Launch Condition – pozwala na tworzenie nowych warunków, które muszą być spełnione przed instalacją. Mogą być one oparte o warunki wyszukiwania lub inne kryteria (np. wersja systemu operacyjnego).

Po stworzeniu nowego warunku, możemy we właściwości Condition ustawić nasz warunek. Np. sprawdzanie wersji IIS może odbyć się w taki sposób:

   1: IISVERSION = "#6" 

Sprawdzamy tu, czy wersja IIS jest równa 6. Oczywiście możemy używać operatorów > < >= <=. Aby sprawdzić czy dyskiem domowym jest C:

   1: %HOMEDRIVE = "C:"

Dodawanie własnej strony setup’u

Możemy dodać własne strony do naszego instalatora. Można w nich pobierać jakieś informacje a następnie przekazywać je jako parametry do własnych akcji. Działania jakie możemy wykonywać podczas instalacji:
Wyświetlanie licencji – projekt Web Setup dostarcza szablon do potwierdzania licencji
Modyfikowanie ustawień w Web.Config – zmienianie ustawień Web.Config na podstawie danych przekazanych przez użytkownika
Wykonywanie niestandardowej konfiguracji – możemy za pomocą własnej konfiguracji zapytać użytkownika o jakieś informację i przechowywać je np. w rejestrze.
Aktywacja bądź rejestracja aplikacji – możemy wymagać od użytkownika podanie klucza bądź rejestracji

Aby dodać własną stronę, wchodzimy w User Interface(prawym przyskiem myszy na projekcie –> View –> User Interface). Edytor ten wyświetla różne etapy konfiguracji itp:
image

Teraz wystarczy PPM na etapie, w którym chcemy dodać stronę i wybieramy Add Dialog. Widzimy opcję, które możemy dodać do projektu. Proponuję pobawić się nimi i zobaczyć jak działają bądź doczytać o nich na msdnie.
image

Wdrażanie aplikacji

Po skonfigurowaniu naszego projektu i zbudowaniu jesteśmy gotowi wdrożyć go na serwerze :) Aby to zrobić, należy użyć plików wygenerowanych podczas buildu:
Setup.exe – plik, który instaluje pliki i wprowadza ustawienia, które dodaliśmy w naszym projekcie. Podczas instalacji zostaniemy zapytani o wszystkie dodatkowe ustawienia.
<NazwaProjektu>.msi – plik instalatora Windows zawierający wszystkie pliki dodane do projektu. Uruchomienie tego pliku jest równoważne uruchomieniu Setup.exe. Ten typ pliku jest mniejszy i bardziej uniwersalny.
Bardziej polecany jest plik msi, chociaż ma jedną wadę: serwer, na którym chcemy instalować aplikację musi mieć Windows Installer.

To wszystko z mojej strony :) Dziękuję za wytrwanie w tej serii wpisów :)

 

Tagi: , , , , ,

70-562:Using ASP.NET Membership

Artykuł pochodzi w serii przygotowań do egzaminu 70-562 ASP.NET.

W dzisiejszym artykule powiemy sobie o używaniu kontrolki logowania, zarządzaniu użytkownikami, informacjami na ich temat oraz o rolach. To wszystko zrealizujemy używając mechanizmu Membership.

Użycie WSAT do konfigurowania zabezpieczeń

Do zarządzania użytkownikami, rolami oraz zabezpieczeniami na naszej stronie możemy użyć wbudowanego narzędzia Web Site Administration Tool (WSAT).

Tworzenie użytkowników

Aby uruchomić WSAT należy wybrać z menu ASP.NET Configuration. Konfiguracje użytkowników znajdziecie w zakładce Security. Najpierw musimy skonfigurować uwierzytelnianie. Mamy do wyboru uwierzytelnianie Windows, Lokalne Active Directory oraz uwierzytelnianie za pomocą formularza. Po wybraniu interesującego nas uwierzytelniania możemy stworzyć użytkownika, zarządzać nim, nadać mu role itp. Poniżej przykład tworzenia nowego usera:

image 

Jeśli wybraliśmy uwierzytelnianie oparte o formularze (Web-based), WSAT automatycznie stworzy lokalną baze i umieści ją w katalogu App_Data (no chyba że chcemy podczepić inną baze np. wygenerowaną tak jak w poprzednim wpisie). Doda również wpis do pliku Web.config, który włącza nasze uwierzytelnianie:

   1: <configuration>
   2:  <system.web>
   3:   <authentication mode="Forms" />
   4:  </system.web>
   5: </configuration>
Tworzenie ról

Aby włączyć role wystarczy odpalić WSAT i kliknąć w link, który je włącza :) Po włączeniu ról, WSAT doda do Web.Config taki kod:

   1: <configuration>
   2:  <system.web>
   3:   <roleManager enabled="true" />
   4:  </system.web>
   5: </configuration>

Teraz możemy już tworzyć własne role i przypisywać do nich użytkowników. Robi się to podczas tworzenia nowego użytkownika ew. podczas jego edycji :)

Podobnie działa mechanizm “Acces Rules”, dzięki któremu możemy określić kto ma dostęp do folderu czy pliku. Również wszystko robimy z poziomu WSAT.

Kontrolka Login

Istnieje kilka kontrolek, wbudowanych w ASP .NET, dzięki którym możemy zarządzać logowaniem i informacjami na temat logowania. Dostarczają one interfejs użytkownika i szereg metod pomagających w zarządzaniu informacjami. Poniżej hierarchia:

image

 

CreateUserWizard – jest to “kreator”, dzięki któremu stworzymy nowego użytkownika.
Login – kontrolka, dzięki której dajemy użytkownikowi możliwość zalogowania się .
LoginView – kontrolki tej możemy użyć np. do udostępnienia linka, dostępnego dla zalogowanych użytkowików
LoginName – wyświetla bieżącego użytkownika (jeśli jest zalogowany)
PasswordRecovery – pozwala na zresetowanie hasła użytkownika poprzez wysłanie maila bądź odpowiedź na pytanie
ChangePassword – pozwala zmienić bieżące hasło.

Wszystkie kontrolki są bardzo proste w użyciu, więc nie będę opisywał po kolei (tak jak jest to opisane w TK), jak przeciągnąć na formatkę itp :) Myślę że każdy sobie z tym poradzi.

Klasa Membership

Poniżej najważniejsze statyczne metody klasy Membership, dzięki którym możemy ręcznie stworzyć użytkownika, usunąć itp:
CreateUser – dodaje nowego użytkownika do bazy danych.
DeleteUser – usuwa użytkownika z bazy
FindUsersByEmail – metoda wyszukuje użytkownika (z kolekcji użytkowników), do którego pasuje podany mail
FindUsersByName – tak jak wyżej tylko szukanie odbywa się po nazwie.
GeneratePassword – metoda ta tworzy losowe hasło o podanej długości
GetAllUsers – zwraca kolekcję wszystkich użytkowników znajdujących się w bazie
GetNumberOfUsersOnline – metoda zwraca ilość zalogowanych osób
GetUser – zwraca aktualnie zalogowanego użytkownika
GetUserNameByEmail – pobiera nazwę użytkownika na podstawie maila
UpdateUser – aktualizuje informacje o użytkowniku

Klasa Roles

Klasa Roles dostarcza wielu statycznych metod, dzięki którym możemy operować, zarządzać rolami.

AddUserToRole, AddUsersToRole i AddUsersToRoles – dodają użytkownika (użytkowników) do roli (ról)
CreateRole – tworzy nową role
DeleteRole – kasuje role
FindUsersInRole – znajduje użytkowników w podanej roli
GetAllRoles – zwraca wszystkie istniejące role
GetRolesForUser – zwraca role przypisane do podanego użytkownika
IsUserInRole – zwraca true, jeśli użytkownik należy do podanej roli
RemoveUserFromRole, RemoveUsersFromRole, RemoveUserFromRoles, i RemoveUsersFromRoles – usuwa użytkownika z roli

Jeśli np. chcemy przypisać użytkownika do roli, możemy napisać tak: (w tym przykładzie nazwa użytkownika brana jest z kontrolki CreateUserWizard):

   1: Roles.AddUserToRole(CreateUserWizard1.UserName, "Users");

To tyle na dziś. W następnym artykule będziecie mogli przeczytać o zabezpieczaniu strony. Zapraszam w imieniu Dawida :)

Tagi: , , , , , ,

70-562:Configuring globalization and Localization

Artykuł pochodzi w serii przygotowań do egzaminu 70-562 ASP.NET.

W dzisiejszym artykule powiemy sobie o działaniu zasobów w ASP .NET, poznamy jak za ich pomocą robić np. wielojęzykową strone.

Zasoby

Do obsługi języków na stronie, ASP .NET wykorzystuje pliki zasobów (ang. resource files). Plik zasobów zawiera zestaw tekstów (specyficznych dla danego języka), dla strony lub całej witryny. Jeśli plik z zasobami istnieje dla języka, który jest pożądany przez użytkownika, ASP korzysta z niego. Jeśli plik nie istnieje, wyświetla domyślny język, który ustawiliśmy na naszej stronie.  Istnieją dwa typy zasobów: lokalne i globalne. Omówione zostaną w dalszej części artykułu.

Użycie lokalnych plików zasobów

Lokalne zasoby (ang. local resources), są przeznaczone dla pojedynczych stron i powinny być użyte do dostarczania wersji strony w różnych językach. Zasoby te powinny być umieszczone w specjalnym folderze App_LocalResources. Każdy plik zasobów jest określony dla danej strony, dlatego nazywa się go w taki sposób: <NazwaStrony>.Aspx.resx. Np. dla strony o nazwie Default.aspx, plik zasobów będzie nazywał się Default.aspx.resx. Dzięki temu określiliśmy domyślny plik zasobów.
Możemy stworzyć plik zasobów, który określa nam dany język, za pomocą pliku zasobów o nazwie: <NazwaStrony>.aspx.<jezyk>.resx. Np. jeśli chcemy hiszpańską wersję strony nasz plik będzie wyglądał tak: Default.aspx.es.resx. (es jest skrótem Hiszpanii).

Generowanie lokalnych zasobów

Do wygenerowania naszego pliku zasobów możemy użyć Visual Studio, które wygeneruje nam domyślną wersję pliku. Możemy zrobić to w następujący sposób:
1. Otwieramy naszą stronę w visual studio
2.  Z menu Tools wybieramy Generate Local Resource
    W tym momencie Visual Studio stworzył folder App_LocalResources oraz stworzył plik XML. Plik bedzie   zawierał ustawienia zasobów dla strony oraz kontrolował właściwości takie jak Text,ToolTip,Title,Caption itp.
W naszym przykładzie tworzymy sobie stronę zawierającą kontrolki: label, textbox, calendar oraz button.

image

Poniżej plik zasobów dla naszej przykładowej strony. Plik został otworzony w wizualnym edytorze plików zasobów, który udostępnia nam Visual Studio.
image

Możemy również otworzyć ten plik w edytorze XML. Widzimy tu m.in taki kod:

   1: <data name="ButtonSubmitResource1.Text" xml:space="preserve">
   2:  <value>Save</value>
   3: </data>
   4: <data name="ButtonSubmitResource1.ToolTip" xml:space="preserve">
   5:  <value />
   6: </data>

Widzimy pary data-value. W data trzymamy właściwość kontrolki np. Text a w value jej wartość.

Tworzenie wersji językowych zasobów lokalnych

Aby stworzyć językowy plik zasobów musimy najpierw stworzyć domyślny plik zasobów. Następnie:
1. W solution explorer klikamy prawym przyciskiem na naszym domyślnym pliku zasobów i wybieramy Copy
2. Kopiujemy ten plik do folderu App_LocalResources
3. Zmieniamy nazwę pliku na np. Default.aspx.fr.resx aby stworzyć francuską wersję.
4. Klikamy na nasz plik i zmieniamy wartości na własne – francuskie :D

Zasoby globalne

Globalne zasoby (ang. global resources) to takie, które można odczytać z każdej strony. Zasoby te przydają się w momencie, gdy chcemy uzyskać dostęp do zasobu z wielu stron w naszej witrynie.
Globalne zasoby są wciąż plikami .resx i wciąż tworzymy domyślną wersję a później jej odmiany językowe. Tak samo jest z nazewnictwem plików. Różnią się jednak położeniem plików. Globalne zasoby trzymamy w folderze App_GlobalResource. Tworzenie plików wygląda tak jak poprzednio. Również mamy edytor itp. dlatego nie będę tu opisywał od nowa tworzenia wszystkiego.

To tyle na dzisiaj. W tym rozdziale TK nie bardzo pokazał jak ustawiać w kodzie, z której wersji strony chcemy korzystać. Podejrzewam że pojawi się to już w piątek :)

Tagi: , , , , , ,

70-562: Using the asp.net intrinsic Objects

Artykuł pochodzi w serii przygotowań do egzaminu 70-562 ASP.NET.

Korzystając z obiektów wbudowanych w ASP .NET, możemy uzyskać dostęp do wielu przydatnych informacji na temat naszej aplikacji, serwera itp. Obiekty te nazwane zostały ASP .NET

Page i Application Context

Poniżej lista obiektów (przedstawiona jako właściwości obiektu Page), kóre możemy używać do uzyskiwania informacji dotyczących strony i kontekstu aplikacji.
 

Obiekt Opis
Response

Instancja klasy System.Web.HttpResponse. Daje dostęp do HTTP response wysłanego z serwera do klienta po otrzymaniu nadchodzącego żądania. Możemy go użyć do wyświetlania cookies, przekierowania usera itp.

Request Instancja klasy System.Web.HttpRequest. Daje dostęp do informacji, które są częścią bieżącego żądania strony, zawiera ciasteczka, certyfikaty itp
Server Instancja klasy System.Web.HttpServerUtility. Przedstawia metody, które możemy użyć do kontrolowania transferu między stronami, zawiera informację o najnowszych błędach, koduje i dekoduje HTML.
Context Instancja klasy System.Web.HttpContext. Daje dostęp do całego bieżącego kontekstu. Większość metod i właściwości dostarczanych przez Context jest również dostarczane przy innych często używanych obiektach takich jak Request czy Server
Session Instancja klasy System.Web.HttpSessionState. Dostarcza informacji na temat bieżącej sesji usera.
Application Instancja klasy System.Web.HttpApplicationState. Daje dostęp do metod i zdarzeń aplikacji dla  wszystkich sesji.
Trace Instancja klasy System.Web.HttpWebTraceContext.

Obiekty Response

Właściwość Page.Response jest obiektem HttpResponse, który pozwala na dodanie danych do HTTP Response i wysłania z powrotem do klienta. Poniżej kilka użytecznych metod:

Metoda Opis
BinaryWrite Wpisuje znaki binarne do odpowiedzi HTTP.
ClearContent Kasuje zawartość ze strumienia, nie zawiera nagłówków HTTP
ClearHeaders Kasuje nagłówki ze strumienia
End Kompletuje odpowiedź i zwraca stronę dla usera
TransmitFile Zapisuje plik do odpowiedzi HTTP bez buforowania
Write Zapisuje informację do odpowiedzi HTTP z buforowaniem
Expires Liczba minut pozostała do momentu, gdy przeglądarka powinna zatrzymać “cach’owanie “  strony.

Oczywiście metod jest dużo więcej ale odsyłam do msdn po więcej informacji.

Obiekty Request

Tak jak powyżej, kilka przydatnych metod Page.Request.

Metoda Opis
SaveAs Zapisuje żądanie do pliku
Headers Pobiera kolekcję nagłówków HTTP
IsAuthenticated Boolowska wartość, mówiąca o tym czy klient jest autoryzowany.
IsLocal Boolowska wartość mówiąca o tym, czy pochodzi z locala
QueryString Kolekcja “query stringów” czyli np. możemy sczytać sobie id z adresu sczytać.
TotalBytes Długość żądania
UserHostAddress Adres IP klienta
PhysicallPath Fizyczna ścieżka do bieżącego żądania

Obiekty Server

Przydatne metody Page.Server:

Metoda Opis
ClearError Czyści ostatni błąd
GetLastError Zwraca poprzedni wyjątek
HtmlDecode Usuwa znaczniki HTML ze stringa
MapPath Zwraca ścieżkę do pliku, który odpowiada specyficznej ścieżce na serwerze
UrlDecode Dekoduje stringi zakodowane dla transmisji HTTP i wysyła do serwera w URL.
UrlEncode Koduje stringi dla transmisji HTTP

Obiekty Context

Metoda Opis
AddError Dodaje wyjątek do strony, który później może być pobierana przez wywołanie Server.GetLastError
ClearError Czyści ostatni błąd
AllErrors Kolekcja nieobsłużonych wyjątków
IsCustomErrorEnabled Boolowska wartość, która jest true jeśli własne błędy są obsługiwane
IsDebuggingEnabled Boolowska wartość, która jest true jeśli debugowanie jest włączone

Określanie typu przeglądarki

Aby upewnić się, że nasze strony są wyświetlane poprawnie, bardzo ważne jest upewnienie się czy każdy rodzaj przeglądarki wyświetli nasze komponenty itp. Jeżeli korzystamy głównie z ASP .NET, mamy dużą pewność, że nie będzie problemu z przeglądarkami ponieważ komponenty ASP .NET same dostosowują się do różnych typów. Jeśli jednak używamy elementów z DHTML-a, JavaScript oraz CSS, możemy mieć w przyszłości problemy z wyświetlaniem na różnych przeglądarkach.
Aby zapobiec różnym nie fajnym sytuacjom należy sprawdzać różne ustawienia przeglądarki. Do tego celu służy “HttpBrowserCapabilities object” i różne właściwości w nim zawarte m.in:

Właściwość Opis
ActiveXControls Pobiera wartość, która wskazuje czy przeglądarka obsługuje ActiveX
AOL Pobiera wartość, która wskazuje czy klientem jest przeglądarka AOL
BackgroundSounds Pobiera wartość, która mówi o tym czy przeglądarka wspiera odtwarzanie muzyki w tle używając taga <bgsounds>
Cookies Sprawdzanie czy włączone są ciasteczka
Frames Sprawdzanie, czy obsługiwane są ramki
JavaScript Sprawdzanie, czy obsługiwany jest JS
JScriptVersion Zwraca wersję JavaScript obsługiwaną przez przeglądarke
JavaApplets Sprawdzanie, czy są obsługiwane aplety java
ClrVersion Sprawdzanie wersji .NET zainstalowanej u klienta

 To wszystko na dzisiaj. Jak widzimy artykuł bardziej teoretyczny. Polecam zajrzenie do MSDN i poczytanie dodatkowo o wszystkich rzeczach, o których dzisiaj mówiliśmy :)

Do piątku :)

Tagi: , , , , , , ,

70-562: Working with user controls

Artykuł pochodzi w serii przygotowań do egzaminu 70-562 ASP.NET.

W dzisiejszym artykule powiemy sobie o tworzeniu kontrolki użytkownika oraz używaniu jej w naszej stronie internetowej.

Kontrolka użytkownika w ASP .NET jest to plik z rozszerzeniem .ascx, w którym grupujemy np. inne kontrolki dostępne w ASP i ustalamy im jakieś zachowanie. Plik z kontrolką jest bardzo podobny do normalnego pliku ze stroną (.aspx) i również posiada “code-behind”. Kontrolkę użytkownika można wykorzystywać na raz w kilku różnych stronach.

Tworzenie kontrolki

Kontrolka użytkownika, którą tworzymy, dziedziczy po klasie UserControl. Ta klasa z kolei dziedziczy po TemplateControl. Pełna hierarchia poniżej:

 

image

Aby stworzyć plik z kontrolką musimy kliknąć prawym przyciskiem myszy na projekt –> Add –> New Item, i wybrać Web User Control. Po utworzeniu pliku (z rozszerzeniem .ascx) widzimy, że jest on bardzo podobny do zwykłej “Web page”. Jednak na górze zamiast dyrektywy @Page jest @Control. Poniżej cały kod:

   1: <%@ Control Language=”C#" AutoEventWireup=”true”
   2:     CodeFile=" MyWebUserControl.ascx.cs” Inherits=" MyWebUserControl” %>

Teraz możemy już przeciągać kontrolki tak jak przy zwykłej stronie. Np. możemy przeciągnąc sobie jakiś textbox, czy button tworząc w ten sposób kontrolkę do adresu. Wszystko może wyglądać tak:
image

Definiowanie zdarzeń

Kontrolka użytkownika może posiadać swoje własne, zamknięte zdarzenia. Zdarzenia te są wywoływane synchronicznie razem z tymi wywoływanymi na stronie.
W poprzednim przykładzie przygotowaliśmy sobie widok kontrolki. Teraz warto by było oprogramować przycisk, który może np. zapisywać adres do profilu zalogowanego użytkownika. Na poniższym przykładzie zrealizowane zostało właśnie takie zapisywanie, za pomocą własnej klasy UserProfile.

   1: protected void ButtonSave_Click(object sender, EventArgs e)
   2: {
   3:  //to do: walidacja
   4:  //zapisywanie nowego adresu dla usera
   5:  UserProfile.AddNewAddress(this.UserId, this.AddressType,
   6:   TextBoxAddress1.Text, TextBoxAddress2.Text,
   7:   TextBoxCity.Text, TextBoxState.Text, TextBoxZip.Text);
   8: }

Definiowanie właściwości

Tworząc kontrolki często będziemy musieli konfigurować dane. Konfigurowanie danych w kontrolce możemy zdefiniować za pomocą właściwości, które następnie będą mogły być konfigurowane przez znaczniki na stronie, która używa kontrolki.
Właściwości tworzymy tak jak w innych klasach .NET Frameworka. Np. Aby dodać właściwości UserId oraz AddressType do napisanej przez nas kontrolki piszemy po prostu:

   1: public int UserId { get; set; }
   2: public UserProfile.AddressType AddressType { get; set; }

Dodawanie kontrolki do strony

Możemy dodać naszą kontrolkę do strony przez przeciągnięcie jej z Solution Explorera. Po dodaniu kontrolki, zobaczymy w Design View jej zawartość. Od strony source view kod prezentuje się następująco:

   1: <%@ Page Language=”VB” AutoEventWireup=”false”
   2:     CodeFile=”UserProfilePage.aspx.vb” Inherits=”UserProfilePage” %>
   3: <%@ Register src=”AddressUc.ascx” tagname=”AddressUc” tagprefix=”uc1” %>
   4: <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN
   5:http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd>
   6: <html xmlns=”http://www.w3.org/1999/xhtml>
   7:  <head runat=”server>
   8:   <title>User Profile Settings</title>
   9:  </head>
  10: <body style=”font-family: Verdana; font-size: small>
  11:  <form id=”form1runat=”server>
  12:   <div>
  13:    <uc1:AddressUc ID=”AddressUc1runat=”serverAddressType=”Home/>
  14:   </div>
  15: </form>
  16: </body>
  17: </html>

 

Widzimy nową dyrektywę @Register, która jest wymagana do zarejestrowania kontrolki na stronie. Atrybut TagPrefix jest to identyfikator, do którego będziemy się odwoływać, żeby stworzyć kontrolkę na stronie. Samo stworzenie kontrolki widzimy w sekcji <div> </div>. Widzimy również atrybut AddressType, który zdefiniowaliśmy sobie w poprzednim przykładzie, jako właściwość.

Dynamiczne ładowanie kontrolki

Aby dynamicznie ładować naszą kontrolkę musimy użyć metody LoadControl. Metoda ta przyjmuje nazwę oraz ścieżkę do pliku, który zawiera definicję kontrolki. Metoda ta zwraca również referencję do kontrolki.
Przykładowo, załóżmy, że chcemy dodać kilka instancji naszej kontrolki AdressUc. Poniższy kod pokazuje jak dynamicznie wczytywać kontrolkę i dodać do form’a:

   1: AddressUc addressControl = (AddressUc)LoadControl(“AddressUc.ascx”);
   2: form1.Controls.Add(addressControl);

 

Tworzenie Templated User Control

Templated user control umożliwia separacje danych od ich prezentacji. Nie dostarcza natomiast domyślnego layoutu. Layout jest dostarczany przez developera.
Proces tworzenia możemy opisać w kilku krokach:
1. Dodajemy plik kontrolki do projektu
2. Wstawiamy wewnątrz naszej kontrolki, kontrolkę Placeholder, która definiuje miejsce dla szablonu. Później będzie można przedstawić to jako właściwość.
3. Definiujemy klasę w naszej aplikacji, która będzie kontenerem nazw. Ta klasa będzie zawierała referencje do danych naszej kontrolki. Klasa dziedziczy po Control oraz implementuje interfejs INamingContainer.
4. W code behind kontrolki implementujemy właściwość typu ITemplate.
5. Następnie należy dodać kod do metody Page_Init w naszej kontrolce. W tym miejscu testujemy powyższą właściwość. Jeśli jest ustawiona, tworzymy instancję klasy z pkt 3.

Powyższe kroki są dosyć ogólne. Stwórzmy teraz własną implementację kontrolki, bazując na tej, którą tworzyliśmy od początku artykułu. Zaczynamy od placeholder’a w naszej kontroce:

   1: <%@ Control Language=”C#" AutoEventWireup=”true”
   2:   CodeFile=”AddressUcTemplated.ascx.cs” Inherits=”AddressUcTemplated” %>
   3: <asp:PlaceHolder runat=”serverID=”PlaceHolderAddressTemplate>
   4: </asp:PlaceHolder>

Następnie dodajemy kod do code-behind kontrolki. Kod zawiera właściwość ITemplate oraz metode Page_Init.
ITemplate używamy w celu okreslenia obszaru dla usera. Metodzie Page_Init służy do utworzenia instancji kontenera nazw i połączenia go z layoutem.

   1: public partial class AddressUcTemplated :
   2: System.Web.UI.UserControl
   3: {
   4:  protected void Page_Init(object sender, EventArgs e)
   5:   {
   6:    //czyszczenie kontrolek z place holdera
   7:    PlaceHolderAddressTemplate.Controls.Clear();
   8:    if (LayoutTemplate == null)
   9:     {
  10:      PlaceHolderAddressTemplate.Controls.Add(
  11:         new LiteralControl(“No template defined.")); 
  12:     }
  13:   else
  14:    {
  15:     AddressUcContainer container = new AddressUcContainer(this.Address);
  16:     this.LayoutTemplate.InstantiateIn(container);
  17:     //dodanie kontrolki do placeholdera
  18:     PlaceHolderAddressTemplate.Controls.Add(container);
  19:    }
  20:  }
  21: [PersistenceMode(PersistenceMode.InnerProperty)]
  22: [TemplateContainer(typeof(AddressUcContainer))]
  23:  public ITemplate LayoutTemplate { get; set; }
  24:  public Address Address { get; set; }
  25: }

Ostatnim krokiem jest zdefiniowanie kontenera nazw.  W tym wypadku klasa dziedziczy po Control oraz implementuje interfejs INamingContainer. Wszystko wygląda tak:

   1: public class AddressUcContainer : Control, INamingContainer
   2: {
   3:  public AddressUcContainer(Address address)
   4:   {
   5:    this.Address = address;
   6:   }
   7:  public Address Address { get; set; }
   8: }

Używanie Templated User Control

Podobnie jak przy zwykłej kontrolce użytkownika, templated user control przeciągamy na naszą stronę. Dyrektywa wygląda wtedy w ten sposób:

   1: <%@ Register src=”AddressUcTemplated.ascx”
   2:    tagname=”AddressUcTemplated” tagprefix=”uc1” %>

Następnie musimy stworzyć wygląd dla naszej kontrolki. Robi się to w tagach <LayoutTemplate>, które są zdefiniowane we właściwości ITemplate. Wewnątrz szablonu możemy odwoływać się do naszego kontenera za pomocą obiektu Container. Nasz szablon prezentuje się tak:

   1: <uc1:AddressUcTemplated ID=”AddressUcTemplated1
   2:    runat=”serverAddressType=”Home>
   3:   <LayoutTemplate>
   4:    <h1>Edit Home Address</h1>
   5:     <table>
   6:     <tr>
   7:       <td>Address Line 1:</td>
   8:       <td>
   9:        <asp:TextBox ID=”TextBoxAddressrunat=”server
  10:         Text="<%#Container.Address.AddressLine1%>"></asp:TextBox>
  11:       </td>
  12:     </tr>
  13:     <tr>
  14:     <td>Address Line 2:</td>
  15:      <td>
  16:         <asp:TextBox ID=”TextBoxAddressLine2runat=”server
  17:         Text="<%#Container.Address.AddressLine2%>"></asp:TextBox>
  18:      </td>
  19:      </tr>
  20:      <tr>
  21:        <td>City:</td>
  22:         <td><asp:TextBox ID=”TextBoxCityrunat=”server
  23:          Text="<%#Container.Address.City%>"></asp:TextBox>
  24:         </td>
  25:      </tr>
  26:      <tr>
  27:       <td>State:</td>
  28:       <td>
  29:         <asp:TextBox ID=”TextBoxStaterunat=”server
  30:          Text="<%#Container.Address.State%>"></asp:TextBox>
  31:        </td>
  32:      </tr>
  33:      <tr>
  34:         <td>Zip:</td>
  35:         <td><asp:TextBox ID=”TextBoxZiprunat=”server
  36:           Text="<%#Container.Address.Zip%>"></asp:TextBox>
  37:         </td>
  38:        </tr>
  39:        <tr>
  40:       <td></td>
  41:       <td>
  42:     <asp:Button ID=”ButtonSaverunat=”serverText=”Save/>
  43:     </td>
  44:     </tr>
  45: </table>
  46: </LayoutTemplate>
  47: </uc1:AddressUcTemplated>

Musimy jeszcze dodac do code-behind. Kod powinien wyłować metodę Page.DataBind aby zapewnić, że kontener jest powiązny z layoutem.

   1: protected void Page_Load(object sender, EventArgs e)
   2: {
   3:  //symulacja wpowadzania danych
   4:  AddressUcTemplated1.Address.AddressLine1 = “1234 Some St.";
   5:  AddressUcTemplated1.Address.City = “Ann Arbor”;
   6:  AddressUcTemplated1.Address.State = “MI”;
   7:  AddressUcTemplated1.Address.Zip = “48888”;
   8: //bindowanie danych
   9: Page.DataBind();
  10: }

Wszystko wygląda tak:

image

 

To wszystko na dziś. Zapraszam na kolejny artykuł z serii, który pojawi się, jak zwykle, w piątek.

Tagi: , , , , ,

70-562:Working with data-bound Web server controls

Artykuł pochodzi w serii przygotowań do egzaminu 70-562 ASP.NET.

W dzisiejszym artykule zostanie zaprezentowane wiązanie danych w ASP .NET.

Wprowadzenie do wiązania danych

Wiązanie danych w ASP .NET może być klasyfikowane w proste, złożone bądź hierarchiczne kontrolki. Proste  kontrolki są to takie, które dziedziczą z ListControl. Złożone są klasami dziedziczącymi z CompositeDataBoundControl, (takie jak GridView, DetailsView, FormsView itp). Hierarchiczne to kontrolki Menu oraz TreeView.
.NET Framework dostarcza kilka klas bazowych, które są stosowane do dostarczania wspólnych właściwości oraz zachować do konkretnych kontrolek. W ten sposób wiązanie danych wszędzie wygląda podobnie.
Poniżej rysunek przedstawiający hierarchię klas bazowych, wykorzystywanych do wiązania danych:
image 

BaseDataBoundControl jest pierwszą kontrolką w hierarchii, która dziedziczy po WebControl. Zawiera właściwości DataSource oraz DataSourceId potrzebne podczas wiązania danych. Właściwość DataSoruce pobiera bądź ustawia obiekt,z którego kontrolka data-bound pobiera dane.
Następną kontrolką jest HierarchicalDataBoundControl, dziedzicząca z BaseDataBoundControl. Jest rodzicem dla kontrolek takich jak Menu czy ListView.

Proste kontrolki Data-Bound

Istnieje wiele kontrolek w ASP .NET zawierających podstawowe, oparte na listach wiązanie danych. Kontrolki te nie są przeznaczone do pracy z danymi, w sensie ich edytowania itp. Dostarczają za to listy danych, z którymi możemy pracować. Poniżej rysunek hierarchii klasy ListControl:

image

 

Klasa ListControll jest abstrakcyjną klasą bazową, która dostarcza wspólną funkcjonalność dla klas dziedziczących po niej. Zawiera kolekcję Items, która jest kolekcją obiektów ListItem. Każdy ListItem zawiera właściwość Text, która jest wyświetlana użytkownikowi oraz Value, która jest wysyłana przy post backu do serwera.
Możemy dodawać elementy do ListItems w kodzie lub deklaratywnie w znacznikach. Możemy również wiązać dane ustawiając właściwość DataSource bądź DataMember jeśli źródło danych posiada więcej niż jedną tabele. Poniższy przykład pokazuje jak zbindować dane z kontrolką ListBox:

   1: <asp:ListBox
   2:   ID="ListBox1"
   3:   runat="server"
   4:   //podajemy Id źródła danych
   5:   DataSourceID="SqlDataSource1"
   6:   //tu jaki ma być tekst na wyświetlanym polu
   7:   DataTextField="CompanyName"
   8:   //tu wartość
   9:   DataValueField="ShipperID">
  10: </asp:ListBox>

 

Kontrolka DropDownList

Kontrolka DropDownList służy do wyświetlania listy, z których użytkownik może wybrać sobie jakąś wartość. Aby określić jaki przedmiot z listy wybrał użytkownik możemy użyć właściwości SelecValue, SelectedItem bądź SelectedIndex.
Poniższy przykład pokazuje jak zbindować DropDownList z SqlDataSource, który zwraca tabelę Territorities z bazy danych Northwind.

   1: <asp:DropDownList runat="server" Width="250px" 
   2:  ID="DropDownList1"
   3:  DataSourceID="SqlDataSource1"
   4:  DataTextField="TerritoryDescription"
   5:  DataValueField="TerritoryID" >
   6: </asp:DropDownList>

Widzimy, że jako tekst będzie wyświetlany opis Territority a jako wartość ID.
Aby przechwycić wartość wybraną przez użytkownika możemy napisać tak:

   1: //przypisanie wybranej wartośći do labela
   2: Label1.Text = "You selected TerritoryID: " + DropDownList1.SelectedValue;

 

Kontrolka ListBox

Dzięki kontrolka ListBox pozwala użytkownikom widzieć więcej danych w jednym czasie niż DropDownList. Może być również skonfigurowana tak, aby można było wybrać z listy więcej niż jeden element. Aby to zmienić należy ustawić właściwość SelectionMode. Kontrolka ta posiada również właściwość Rows, której używamy do określenia ilości wyświetlanych elementów w danym momencie. Poniższy przykład pokazuje jak ustawić wielo wybór oraz pokazać 13 wierszy w ListBox. Podczepione jest również DataSource tak jak w poprzednich przykładach:

   1: <asp:ListBox runat="server" Height="225px" Width="275px"
   2:  ID="ListBox1"
   3:  Rows="13"
   4:  DataSourceID="SqlDataSource1"
   5:  DataTextField="TerritoryDescription"
   6:  DataValueField="TerritoryID"
   7:  SelectionMode="Multiple">
   8: </asp:ListBox>

Aby teraz wyświetlić zaznaczone elementy, musimy przejechać po kolekcji itemów np tak:

   1: foreach (ListItem i in ListBox1.Items)
   2: {
   3:  if(i.Selected)
   4:   Label1.Text = Label1.Text + "You selected TerritoryID: " + i.Value + "<br />";
   5: }

W training kicie wygląda to tak:
image

Kontrolki CheckBoxList oraz RadioButtonList

Tytułowe kontrolki są bardzo podobne. Sądze, że każdy wie do czego służą :) Możemy również powiązać je z DataSource. Wygląda to niemal identycznie jak na poprzednich przykładach. Jedyną nowością jest właściwość RepeatColumns, w której określamy liczbę kolumn wyświetlanych poziomo.

   1: <asp:CheckBoxList runat="server"
   2:  ID="CheckBoxList1"
   3:  DataSourceID="SqlDataSource1"
   4:  DataTextField="TerritoryDescription"
   5:  DataValueField="TerritoryID" RepeatColumns="5">
   6: </asp:CheckBoxList>

Kontrolki Composite Data-Bound

Kontrolki Composite Data-Bound dziedziczą po klasie bazowej CompositeDataBoundControl. Klasa ta implementuje interfejs INamingContainer. Poniżej hierarchia:

image 
Kontrolka GridView

GridView, jak wiemy, służy do wyświetlania danych w postaci tabelki, która jest renderowana w kodzie html.
Składa się on (gridview) z kolekcji obiektów GridBiewRow oraz DataControlField. Obiekty GridViewRow dziedziczą z TableRow, które zawierają właściwość Cell. Ta właściwośc jest kolekcją obiektów DataControlFieldCell. Poniżej rysunek przedstawiający budowę grida:
image

Poniżej hierarchia kontrolki DataControlField. Klasy pochodne są wykorzystywane do tworzenia DataControlFieldCell z właściwą treścią.
image

Training kit poświęcił tu dość sporo miejsca na opisywanie tego, jak zmienić wygląd GridView’a ale myślę że o tym możecie doczytać sobie gdzieś przy okazji bo jest tu mowa również o stylach itp :)
Warto natomiast wspomnieć, że aby zbindować dane z GridView należy również uzupełnić właściwość DataSourceID i wpisać tam id naszego datasource.

Kontrolka DetailsView

Kontrolka DetailsView pozwala na wyświetlenie szczegółowych informacji dotyczących pojedyńczego rekordu. Pozwala również na edytowanie, kasowanie i dodawanie rekordu.
Jako przykład po raz kolejny kontrolka z podanym DataSourceId. Nowością jest właściwość AllowPaging. Jeśli ustawiona jest na true, pozwala kontrolce na samodzielne poruszanie się po źródle danych.

   1: <asp:DetailsView runat="server" Width="300px"
   2:  ID="DetailsView1"
   3:  AllowPaging="True"
   4:  AutoGenerateRows="False"
   5:  DataKeyNames="ProductID"
   6:  DataSourceID="SqlDataSource1">
   7:   <Fields>
   8:    <asp:BoundField DataField="ProductID" HeaderText="ProductID"
   9:      InsertVisible="False" ReadOnly="True" SortExpression="ProductID" />
  10:    <asp:BoundField DataField="ProductName" HeaderText="ProductName"
  11:      SortExpression="ProductName" />
  12:    <asp:BoundField DataField="SupplierID" HeaderText="SupplierID"
  13:      SortExpression="SupplierID" />
  14:    <asp:BoundField DataField="CategoryID" HeaderText="CategoryID"
  15:      SortExpression="CategoryID" />
  16:    <asp:BoundField DataField="QuantityPerUnit" HeaderText="QuantityPerUnit"
  17:      SortExpression="QuantityPerUnit" />
  18:    <asp:BoundField DataField="UnitPrice" HeaderText="UnitPrice"
  19:      SortExpression="UnitPrice" />
  20:    <asp:BoundField DataField="UnitsInStock" HeaderText="UnitsInStock"
  21:      SortExpression="UnitsInStock" />
  22:    <asp:BoundField DataField="UnitsOnOrder" HeaderText="UnitsOnOrder"
  23:      SortExpression="UnitsOnOrder" />
  24:    <asp:BoundField DataField="ReorderLevel" HeaderText="ReorderLevel"
  25:      SortExpression="ReorderLevel" />
  26:    <asp:CheckBoxField DataField="Discontinued" HeaderText="Discontinued"
  27:      SortExpression="Discontinued" />
  28:    <asp:CommandField ShowDeleteButton="True" ShowEditButton="True"
  29:      ShowInsertButton="True" />
  30:  </Fields>
  31: </asp:DetailsView>


Po uruchomieniu wygląda to tak:

image
Kontrolki Hierarchical Data-Bound

Kontrolka HierarchicaldataBoundControl jest klasą bazową dla kontrolek renderujących dane hierarchicznie.
image

Kontrolka TreeView

TreeView wyświetla dane w postaci hierarchicznej tak jak lista plików i folderów. Kontrolka może współpracować również z SiteMapą, jeśli uzupełniona jest właściwość DataSourceID.
Typowa struktura TreeView ma tylko jeden główny węzeł ale nic nie stoi na przeszkodzie, aby dodać więcej takich węzłów :)
TreeView może być wypełniony przy użyciu danych statycznych bądź możemy bindować go z danymi.
Aby wypełnić kontrolkę danymi statycznymi należy umieścić je w sposób deklaratywny w znaczniku <Node>, w elemencie TreeView, a następnie zagnieździć w nim elementy <asp:TreeNode>. Każdy taki element ma właściwości, które można ustawić poprzez dodanie atrybutów.
Aby wypełnić dane poprzez wiązanie, należy użyć jakiegoś źródła danych, które implementuje interfejs IHierarchicalDataSource. Takim źródłem są np. XmlDataSource bądź SiteMapDataSource. Teraz wystarczy ustawić DataSourceID na taki jaki mamy podany w naszym źródłem i już :)

Załóżmy, że chcemy skorzystać z kontrolki do wyświetlenia danych klientów z pliku o nazwie Customers.xml, który zawiera liste klientów, zamówień i faktur a także pozycję każdego zamówienia. Dane te są przechowywane w formacie xml. Plik Customers.xml wygląda tak:

   1: <?xml version="1.0" encoding="utf-8" ?>
   2: <Customers>
   3:  <Customer CustomerId="1" Name="Northwind Traders">
   4:   <Orders>
   5:    <Order OrderId="1" ShipDate="06-22-2006">
   6:     <OrderItems>
   7:     <OrderItem OrderItemId="1" PartNumber="123"
   8:       PartDescription="Large Widget" Quantity="5"
   9:       Price="22.00" />
  10:     <OrderItem OrderItemId="2" PartNumber="234"
  11:       PartDescription="Medium Widget" Quantity="2"
  12:       Price="12.50" />
  13:   </OrderItems>
  14:  </Order>
  15: <Order OrderId="2" ShipDate="06-25-2006">
  16:  <OrderItems>
  17:   <OrderItem OrderItemId="5" PartNumber="432"
  18:    PartDescription="Small Widget" Quantity="30"
  19:    Price="8.99" />
  20:   <OrderItem OrderItemId="4" PartNumber="234"
  21:    PartDescription="Medium Widget" Quantity="2"
  22:    Price="12.50" />
  23:  </OrderItems>
  24: </Order>
  25: </Orders>
  26: <Invoices>
  27:  <Invoice InvoiceId="6" Amount="99.37" />
  28:  <Invoice InvoiceId="7" Amount="147.50" />
  29: </Invoices>
  30: </Customer>
  31: <Customer CustomerId="2" Name="Tailspin Toys">
  32:  <Orders>
  33:   <Order OrderId="8" ShipDate="07-11-2006">
  34:    <OrderItems>
  35:     <OrderItem OrderItemId="9" PartNumber="987"
  36:      PartDescription="Combo Widget" Quantity="2"
  37:      Price="87.25" />
  38:     <OrderItem OrderItemId="10" PartNumber="654"
  39:      PartDescription="Ugly Widget" Quantity="1"
  40:      Price="2.00" />
  41:  </OrderItems>
  42: </Order>
  43: <Order OrderId="11" ShipDate="08-21-2006">
  44:  <OrderItems>
  45:   <OrderItem OrderItemId="12" PartNumber="999"
  46:    PartDescription="Pretty Widget" Quantity="50"
  47:    Price="78.99" />
  48:   <OrderItem OrderItemId="14" PartNumber="575"
  49:    PartDescription="Tiny Widget" Quantity="100"
  50:    Price="1.20" />
  51:  </OrderItems>
  52: </Order>
  53: </Orders>
  54: <Invoices>
  55:  <Invoice InvoiceId="26" Amount="46.58" />
  56:  <Invoice InvoiceId="27" Amount="279.15" />
  57: </Invoices>
  58: </Customer>
  59: </Customers>

XmlDataSource oraz TreeView są dodane do strony oraz skonfigurowane. Poniżej kod kontrolki TreeView:

   1: <asp:TreeView ID="TreeView1" runat="server"
   2:   DataSourceID="XmlDataSource1"
   3:   ShowLines="True" ExpandDepth="0">
   4:    <DataBindings>
   5:     <asp:TreeNodeBinding DataMember="Customer"
   6:       TextField="Name" ValueField="CustomerId" />
   7:     <asp:TreeNodeBinding DataMember="Order"   
   8:       TextField="ShipDate" ValueField="OrderId" />
   9:     <asp:TreeNodeBinding DataMember="OrderItem"
  10:       TextField="PartDescription" ValueField="OrderItemId" />
  11:     <asp:TreeNodeBinding DataMember="Invoice"
  12:       TextField="Amount" ValueField="InvoiceId"
  13:       FormatString="{0:C}" />
  14: </DataBindings>
  15: </asp:TreeView>

Poniżej kod dodany w code-behind, który w prosty sposób wyświetla wybrany węzeł:

   1: public partial class TreeView_Control : System.Web.UI.Page
   2: {
   3:   protected void TreeView1_SelectedNodeChanged(object sender, EventArgs e)
   4:   {
   5:    Response.Write("Value:" + TreeView1.SelectedNode.Value);
   6:   }
   7: }

Wszystko wygląda tak:
image

Kontrolka Menu

Kontrolka Menu najczęściej używana jest w połączeniu z SiteMapDataSource, dzięki której tworzymy nawigację po strnie.
Kontrolka ta podobna jest do poprzedniej. Również możemy uzupełniać ją statycznie oraz dynamicznie. Również aby uzupełniać ją dynamicznie musimy użyć jakiegoś źródła danych, które implementuje interfejs IHierarchicalDataSource.

Poniżej podobny przykład jak w poprzedniej kontrolce. Załóżmy, że chcemy skorzystać z kontrolki Menu, aby wyświetlic menu z danymi z pliku o nazwie MenuItems.xml, który zawiera listę rzeczy do wyświetlenia w menu. Tak wygląda MenuItems.xml:

   1: <?xml version="1.0" encoding="utf-8" ?>
   2: <MenuItems>
   3:  <Home display="Home" url="~/" />
   4:  <Products display="Products" url="~/products/">
   5:  <SmallWidgets display="Small Widgets"
   6:   url="~/products/smallwidgets.aspx" />
   7:  <MediumWidgets display="Medium Widgets"
   8:   url="~/products/mediumwidgets.aspx" />
   9:  <BigWidgets display="Big Widgets"
  10:   url="~/products/bigwidgets.aspx" />
  11: </Products>
  12: <Support display="Support" url="~/Support/">
  13:  <Downloads display="Downloads"
  14:   url="~/support/downloads.aspx" />
  15:  <FAQs display="FAQs"
  16:   url="~/support/faqs.aspx" />
  17: </Support>
  18: <AboutUs display="About Us" url="~/aboutus/">
  19:  <Company display="Company"
  20:   url="~/aboutus/company.aspx" /> 
  21:  <Locations display="Location"
  22:   url="~/aboutus/locations.aspx" />
  23: </AboutUs>
  24: </MenuItems>

Sama kontrolka menu dodana do strony wygląda tak:

   1: <asp:Menu runat="server"
   2:  ID="Menu1"
   3:  DataSourceID="XmlDataSource1"
   4:  OnMenuItemClick="Menu1_MenuItemClick">
   5: </asp:Menu>

Widzimy powiązanie z DataSource oraz określone zostało zdarzenie OnMenuItemClick, które wskazuje na metodę Menu1_MenuItemClick.
Jeszcze na koniec kod, który wyświetli właściwość ValuePath wybranego elementu z MenuItem. Kod umieszczamy w Code-behind:

   1: public partial class Menu_Control : System.Web.UI.Page
   2: {
   3:  protected void Menu1_MenuItemClick(object sender, MenuEventArgs e)
   4:  {
   5:   Label1.Text = e.Item.ValuePath;
   6:  }
   7: }

No i obrazek :)
image

To tyle na dzisiaj. Przyznam szczerze, że ten rozdział w TK jakiś dziwny, bardzo niekonkretny.
Tymczasem zapraszam na kolejny artykuł z naszej serii, który pojawi się, jak zwykle, w piątek.

Tagi: , , , , , , , ,

70-562: Using the ADO.NET Connected Classes

Artykuł pochodzi w serii przygotowań do egzaminu 70-562 ASP.NET.

Witam w dzisiejszym artykule, w którym mowa będzie o dostępie do danych za pomocą klas, które wymagają aktywnego połączenia.

Używanie wbudowanych klas do operowania na danych

Klasy, za pomocą których możemy dodawać, edytować, usuwać itd. dane dostarczone są razem z frameworkiem .NET.
W sklad .NET’u wchodzą dostawcy do danych tacy jak: OleDb, Odbc, SQLServer, Oracl.
Poniższa tabela pokazuję listę podstawowych klas w ADO .NET. W tabeli pokazane zostały klasy bazowe, klasy klienckie oraz interfejsy:
 image

Podstawowe informacje o obiekcie DbConnection

Klasa DbConnection jest klasą abstrakcyjną, po której klasy połączenia, specyficzne dla dostawcy, dziedziczą. Poniżej hierarchia klas:
image

Oraz przykładowy kod, w którym operujemy na obiekcie DbConnection:

   1: DbConnection connection = new SqlConnection();
   2: connection.ConnectionString = "Server=.;Database=pubs;Trusted_Connection=true";
   3: connection.Open();
   4: //jakaś akcja
   5: connection.Close();

Widzimy utworzenie obiektu DbConnection a następnie wykorzystanie metody ConnectionString, która trzyma ścieżkę do naszej bazy danych. Musimy pamiętać, aby otworzyć połączenie zanim wykonamy jakiekolwiek komendy na bazie. Następnie należy połączenie zamknąć :) Możemy to zrobić za pomocą metody Close lub Dispose.

Konfigurowanie Connection String dla ODBC

Odbc jest jedną ze starszych technologii, które wspiera .NET. Poniżej najczęstsze ustawienia connection stringa dla ODBC:
Driver – sterownik ODBC dla połączenia
DSN – nazwa źródła danych, które mogą być konfigurowane przez administratora
Server – nazwa serwera, do którego się łączymy
Trusted_Connection – opis zabezpieczeń
Database – baza danych, do której się łączymy
DBQ - Zazwyczaj fizyczna ścieżka do źródła danych

Konfigurowanie Connection String dla OleDB

Podobnie jak wyżej, najczęstsze ustawienia:
 Data source - Nazwa bazy danych lub fizycznej lokalizacji pliku bazy danych
 File Name - Fizyczna lokalizacja pliku, który zawiera prawdziwy connection string.
Provider – specyficzny sterownik używający connection stringa do łączenia się danymi

Konfigurowanie Connection String dla SQL Server

Training kit przy opisywaniu connection stringa dla SQL Server wymienił bardzo dużo opcji (2,5 strony A4 :D ). Przedstawię kilka z nich:
 Data Source, addr, address, network address, server - Nazwę lub adres IP serwera bazy danych.
Initial Catalog, database – nazwa bazy danych, z której korzystamy
User ID, uid, user – nazwa użytkownika wykorzystywana do łączenia się z SQL Serverem.
 Password, pwd – hasło potrzebne do połączenia się z serwerem.
Workstation ID, wsid - Nazwa komputera klienckiego, który łączy się SQL Server.

Poza powyższymi są jeszcze między innymi: Current Language, language, Application Name, app, Packet Size, Network Library, net, network, Load Balance Timeout, connection lifetime i wiele innych. Zachęcam do doczytania na MSDN.

Dołączanie lokalnego pliku SQL do SQL EXPRESS

Aby dołączyć lokalny plik SQL do SQL Express możemy użyć do tego następującego connection stringa:

   1: Data Source=.\SQLEXPRESS;
   2: AttachDbFilename=C:\MyApplication\PUBS.MDF;
   3: Integrated Security=True;
   4: User Instance=True

 

Ustawiliśmy Data Source na SQL EXPRESS czyli tak jak chcieliśmy oraz w AttachDbFilename wskazaliśmy plik, który chcemy dołączyć.
W praktyce używa się zazwyczaj konfiguratora wbudowanego w SQL Express, który za nas wygeneruje odpowiedni connection string :)

Używanie connection string w Web Configu

Możemy używać connection stringów w pliku konfiguracyjnym www. Dzięki temu w jednym miejscu mamy kontrolę nad połączeniami do bazy co zapewnia łatwiejsze wprowadzanie zmian. Connection stringi możemy umieścić w znaczniku <connectionStrings> tak jak poniżej:

   1: <connectionStrings>
   2: <add name="PubsData" providerName="System.Data.SqlClient"
   3: connectionString="Data Source=.\SQLEXPRESS;
   4: AttachDbFilename=|DataDirectory|PUBS.MDF;
   5: Integrated Security=True; User Instance=True"/>
   6: </connectionStrings>

 

Powyższy przykład dodaje nowy connection string oraz nadaje mu nazwę PubsData. Do powyższego connection stringa możemy się dostać za pomocą klasy ConfigurationManager, tak jak w poniższym przykładzie:

   1: ConnectionStringSettings pubs = 
   2:        ConfigurationManager.ConnectionStrings["PubsData"];
   3: DbConnection connection = new SqlConnection(pubs.ConnectionString);

 

Korzystanie z Visual Studio w celu dodania nowego połączenia

Dzięki Visual Studio dodawanie nowego połączenia do danych staje się prostsze :) Możemy dodać nowe połączenie używając explorera. Otworzy nam się ładny kreator i będziemy mogli określić co chcemy zrobić. Poniżej server explorer po dodaniu nowej bazy danych. Wystarczy kliknąć na explorerze prawym przyciskiem i wybrać dodanie nowego połączenia:

image

Szyfrowanie connection stringa

Często nasze connection stringi zawierają użytkownika i hasło bazy danych. Na szęście z pomocą przychodzi nam narzędzie Aspnet_regiis.exe, dzięki któremu możemy zaszyfrować ważne dane :)
Możemy szyfrować i deszyfrować zawartość pliku Web.config przy użyciu System.Configuration. DPAPIProtectedConfigurationProvider, który korzysta z ochrony danych Windows API (DPAPI)  lub System.Configuration.RSAProtectedConfiguration- Usługodawcy, który wykorzystuje Rivest-Shamir-Adleman (RSA).
Kiedy trzeba korzystać z tego samego zaszyfrowanego pliku konfiguracji na wielu komputerach w sieci Web, należy użyć System.Configuration.RSAProtectedConfigurationProvider, który pozwala na eksport kluczy do szyfrowania danych. Klucze szyfrujące mogą być importowane do innego serwera. Jest to ustawienie domyślne. Typowy plik Web.config może wyglądać tak:

   1: <?xml version="1.0"?>
   2:   <configuration xmlns="http://schemas.microsoft.com/.NetConfiguration/v2.0">
   3:    <appSettings/>
   4: <connectionStrings>
   5:  <add name="ConnectionString" 
   6:    connectionString="Data Source=.\SQLEXPRESS;
   7:    AttachDbFilename=|DataDirectory|\northwnd.mdf;
   8:    Integrated Security=True;User Instance=True"
   9:    providerName="System.Data.SqlClient" />
  10: </connectionStrings>
  11: <system.web>
  12: ...
  13: </system.web>
  14: </configuration>

 

Element connectionStrings może być zaszyfrowany, jeśli uruchomimy polecenie (w konsoli Visual Studio)
z podaniem pełnej ścieżki do witryny sieci Web np:

   1: aspnet_regiis -pef "connectionStrings" "C:\...\EncryptWebSite"

Po wykonaniu polecenia nasz web config może wyglądać tak:

   1: <?xml version="1.0"?>
   2: <configuration xmlns="http://schemas.microsoft.com/.NetConfiguration/v2.0">
   3: <protectedData>
   4: <protectedDataSections>
   5: <add name="connectionStrings"
   6:   provider="RsaProtectedConfigurationProvider"
   7:   inheritedByChildren="false" />
   8: </protectedDataSections>
   9: </protectedData>
  10: <appSettings/>
  11: <connectionStrings>
  12: <EncryptedData Type="http://www.w3.org/2001/04/xmlenc#Element"
  13:    xmlns="http://www.w3.org/2001/04/xmlenc#">
  14: <EncryptionMethod
  15:    Algorithm="http://www.w3.org/2001/04/xmlenc#tripledes-cbc" />
  16: <KeyInfo xmlns="http://www.w3.org/2000/09/xmldsig#">
  17: <EncryptedKey Recipient=""
  18:    xmlns="http://www.w3.org/2001/04/xmlenc#">
  19: <EncryptionMethod
  20:    Algorithm="http://www.w3.org/2001/04/xmlenc#rsa-1_5" />
  21: <KeyInfo xmlns="http://www.w3.org/2000/09/xmldsig#">
  22: <KeyName>Rsa Key</KeyName>
  23: </KeyInfo>
  24: <CipherData>
  25: <CipherValue>PPWA1TkWxs2i698Dj07iLUberpFYIj6wBhbmqfmNK/plarau4i1k+xq5bZzB4VJW8
  26:    OkhwzcIIdZIXff6INJ1wlZz76ZV1DIbRzbH71t6d/L/qJtuOexXxTi2LrepreK/q3svMLpsJycnDPa
  27:    t9xaGoaLq4Cg3P19Z1J6HquFILeo=</CipherValue>
  28: </CipherData>
  29: </EncryptedKey>
  30: </KeyInfo>
  31: <CipherData>
  32: <CipherValue>Q1re8ntDDv7/dHsvWbnIKdZF6COA1y3S91hmnhUN3nxYfrjSc7FrjEVyJfJhl5EDX
  33:    4kXd8ukAjrqwuBNnQbsh1PAXNFDflzB4FF+jyPKP/jm1Q9mDnmiq+NCuo3KpKj8F4vcHbcj+f3GYqq
  34:    B4pYbblAvYnjPyPrrPmxLNT9KDtDr8pDbtGnKqAfcMnQPvA8l5w3BzPM4a73Vtt2kL/z9QJRu3Svd9
  35:    33taxOO/HufRJEnE2/hcBq30WcBmEuXx3LFNjV+xVmuebrInhhxQgM2froBKYxgjwWiWNjIIjIeTI2
  36:    FQ8nZ8V8kzAVohmDYkZpCj4NQGdrjD996h97phI6NnHZYZHJ7oPRz</CipherValue>
  37: </CipherData>
  38: </EncryptedData>
  39: </connectionStrings>
  40: <system.web>
  41: ...
  42: </system.web>
  43: </configuration>

 

Do deszyfrowania możemy użyć poniższego polecenia:

   1: aspnet_regiis -pdf "connectionStrings" "C:\...\EncryptWebSite"

Korzystanie z obiektu DbCommand

DbCommand używamy do przesyłania jednej lub więcej zapytań pisanych w SQL. Może on być jednym z poniższych typów:
 Data Manipulation Language (DML) - Polecenia pobrania, wstawiania, aktualizowania lub usuwania danych
Data Definition Language (DDL) - Polecenia tworzenia tabel i innych obiektów bazy danych lub modyfikacji  schematu bazy danych.
Data Control Language (DCL) - Polecenia, które nadają, blokują, lub cofają uprawnienia

Poniższy kod pokazuje jak utworzyć i zainicjować (ale nie wykonać) DbCommand:

   1: //pobieramy connection stringa
   2: ConnectionStringSettings pubs =
   3: ConfigurationManager.ConnectionStrings["PubsData"];
   4:  
   5: DbConnection connection =
   6:  new SqlConnection(pubs.ConnectionString);
   7: //tworzymy obiekt DbCommand
   8: DbCommand cmd = connection.CreateCommand();
   9:  cmd.CommandType = CommandType.StoredProcedure;
  10:  cmd.CommandText = "uspGetCustomerById";


Ten kod tworzy obiekt SqlConnection i przypisuje go do zmiennej połączenia, która ma typ danych DbConnection. Obiekt DbConnection jest następnie wykorzystywane do tworzenia SqlCommand, który jest przypisany do zmiennej cmd. DbConnection musi być otwarty przed wywołaniem komendy. CommandText zawiera nazwę procedury przechowywanej, a CommandType wskazuje, że jest to nawiązanie do procedury składowanej.

Używanie obiektu DbParameter do przekazywania danych

Gdy zachodzi konieczność przekazania danych do procedury składowanej, należy używać obiektów DbParameter. Na przykład zdefiniowana przez użytkownika procedura składowana o nazwie uspGetCustomerById  może wymagać ID klienta w celu pobrania odpowiednich klientów. Możemy tworzyć obiekty DbParameter za pomocą Parameters.Add tak, jak poniżej:

   1: ConnectionStringSettings pubs =
   2:   ConfigurationManager.ConnectionStrings["PubsData"];
   3:  
   4: DbConnection connection =
   5:   new SqlConnection(pubs.ConnectionString);
   6: DbCommand cmd = connection.CreateCommand();
   7:  cmd.CommandType = CommandType.StoredProcedure;
   8:  cmd.CommandText = "uspGetCustomerById";
   9:  
  10: DbParameter parm = cmd.CreateParameter();
  11:  parm.ParameterName = "@Id";
  12:  parm.Value = "AROUT";
  13:  cmd.Parameters.Add(parm);

 

Używanie metody ExecuteScalar

Można wykonać zapytanie, które ma zwrócić wynik tabeli zawierającej jeden wiersz i kolumnę, takie jak kwerendy, która np. pobiera całkowitą sprzedaż z danego dzinia. W takich sytuacjach, wyniki mogą być traktowane jako jedna wartość. Na przykład, następująca instrukcja SQL zwraca wynik, który składa się z pojedynczego wiersza w jednej kolumnie:

   1: SELECT COUNT(*) FROM Sales

Poniższy kod pokazuje, jak korzystać z metody ExecuteScalar  i łatwo pobrać ilość wierszy w tabeli Sprzedaż w zmiennej o nazwie count:

   1: ConnectionStringSettings pubs =
   2:  ConfigurationManager.ConnectionStrings["PubsData"];
   3:  
   4: DbConnection connection = new SqlConnection(pubs.ConnectionString);
   5:  
   6: DbCommand cmd = connection.CreateCommand();
   7:  cmd.CommandType = CommandType.Text;
   8:  cmd.CommandText = "SELECT COUNT(*) FROM Sales";
   9:  
  10: connection.Open();
  11: int count = (int)cmd.ExecuteScalar();
  12: connection.Close();

Używanie obiektu DbDataReader

Obiekt DbDataReader dostarcza metody wysokiej wydajności do pobierania danych z magazynu danych. Dostarcza dane tylko do odczytu itp. co sprawia, że jest idealny do wypełniania kontrolek ListBox, DropDownList czy nawet GridView, które mają dane tylko do odczytu.
DbDataReader zawiera metodę Read, która pobiera dane do swojego bufora. Tylko jeden
wiersz danych jest zawsze dostępny w danym czasie. Poniższy kod wykorzystuje metodę Load obiektu DataTable w celu wypełnienia DataTable bezpośrednio z obiektu DataReader połączonego z bazą danych:

   1: ConnectionStringSettings pubs =
   2:   ConfigurationManager.ConnectionStrings["PubsData"];
   3: DbConnection connection = new SqlConnection(pubs.ConnectionString);
   4:  
   5: DbCommand cmd = connection.CreateCommand();
   6:  cmd.CommandType = CommandType.Text;
   7:  cmd.CommandText = "SELECT pub_id, pub_name FROM Publishers";
   8:  connection.Open();
   9:    DbDataReader rdr = cmd.ExecuteReader();
  10:    DataTable publishers = new DataTable();
  11:    publishers.Load(rdr, LoadOption.Upsert);
  12:  connection.Close();
  13:  
  14: GridView1.DataSource = publishers;
  15: GridView1.DataBind();

Korzystanie z DbDataAdapter

DbDataAdapter wykorzystywany jest do przetwarzania i aktualizowania danych pomiędzy DataTable i magazynem danych. Poniżej hierarchia:
image

DbDataAdapter posiada właściwość SelectCommand używaną podczas pobierania danych. SelectCommand musi zawierać prawidłowy obiekt DbCommand, który musi mieć ważne połączenie.

Korzystanie z metody Fill

Metoda Fill przenosi dane z magazynu danych do obiektu DataTable, który przekazujesz do tej metody.
Poniższy kod pokazuje jak DataTable mogą być ładowane przy użyciu metody Fill:

   1: ConnectionStringSettings pubs =
   2:  ConfigurationManager.ConnectionStrings["PubsData"];
   3:  
   4: DbConnection connection = new SqlConnection(pubs.ConnectionString);
   5: SqlCommand cmd = (SqlCommand)connection.CreateCommand();
   6:  cmd.CommandType = CommandType.Text;
   7:  cmd.CommandText = "SELECT pub_id, pub_name FROM Publishers";
   8:  
   9: SqlDataAdapter da = new SqlDataAdapter(cmd);
  10:  DataSet pubsDataSet = new DataSet("Pubs");
  11:   da.Fill(pubsDataSet, "publishers");
  12: GridView1.DataSource = pubsDataSet;

Korzystanie z klasy DbProviderFactory

Istnieje wiele powodów do pisania aplikacji, która nie wymaga kodu specyficznego dla dostawcy bazy. Firma może chcieć, np. przejść z programu Microsoft Access do SQL Server itp.
Tu z pomocą przychodzi nam tytułowa klasa. Poniżej hierarchia:
image
Klasy fabryki dostawcy są implementowane jako singletony, Na przykład poniższy kod pokazuje jak utworzyć nowe połączenie za pomocą SqlClientFactory:

   1: //Pobieramy instancję singletonu
   2: DbProviderFactory factory = SqlClientFactory.Instance;
   3: public DbConnection GetProviderConnection()
   4: {
   5:  DbConnection connection = factory.CreateConnection();
   6:   connection.ConnectionString = @"Data Source=.\SQLEXPRESS;"
   7:    + "AttachDbFilename=|DataDirectory|PUBS.MDF;"
   8:    + "Integrated Security=True;User Instance=True";
   9:    return connection;
  10: }

Używanie klasy DbProviderFactories

Aby zapytać o listę dostępnych fabryk, możesz użyć klasy DbProviderFactories. Zawiera ona metodę o nazwie GetFactoryClasses, która zwraca DataTable. Poniżej przykład pokazujący pobranie listy fabryk oraz informacji o nich i przypisanie do grid viewa:

   1: DataTable providersList = null;
   2: providersList = System.Data.Common.DbProviderFactories.GetFactoryClasses();
   3: GridView1.DataSource = providersList;
   4: GridView1.DataBind();

Używanie ADO.NET Transaction Object

Transakcja jest to atomowa jednostka pracy, która musi być wypełniona w całości. Transakcja się powiedzie, jeśli została wykona cała operacja. Transakcji ma cztery podstawowe właściwości: Cząsteczkowość, spójność, izolację i trwałość (tzw. właściwości ACID).
Możemy użyć obiektu DbConnection z metodą BeginTransaction, która tworzy obiekt DbTransaction. Dzięki temu otrzymamy możliwość obsługiwania transakcji. Poniższy kod pokazuje jak to osiągnąć:

   1: ConnectionStringSettings cnSetting = ConfigurationManager.ConnectionStrings["PubsData"];
   2: using (SqlConnection cn = new SqlConnection())
   3: {
   4:  cn.ConnectionString = cnSetting.ConnectionString;
   5:  cn.Open();
   6: using (SqlTransaction tran = cn.BeginTransaction())
   7: {
   8:  try
   9:  {
  10:   //jakis kod
  11: using (SqlCommand cmd = cn.CreateCommand())
  12: {
  13:  cmd.Transaction = tran;
  14:  cmd.CommandText = "UPDATE jobs SET min_lvl=min_lvl * 1.1";
  15:  cmd.ExecuteNonQuery();
  16: }
  17: tran.Commit();
  18: }
  19: catch (Exception xcp)
  20: {
  21:  tran.Rollback();
  22: }
  23: }
  24: }

Używanie LINQ do pracy z danymi

Linq jest to bardzo fajny mechanizm wprowadzony w .NET 3.0, który np. z naszych tabel w bazie danych tworzy obiekty :) Dzięki linq nie tylko możemy się odnosić do baz Sql Serwer ale również XML czy zwykłych obiektów.
Aby zacząć zabawę z linq musimy stworzyć jakąś bazę oraz tabelę w niej. Następnie musimy stworzyć mapowanie bazy. Aby to osiągnąć dodajemy do projektu nowy plik LinqToSql. Następnie z Serwer Explorera przeciągamy nasze tabele na forme. Wygląda to np. tak:
image

Załóżmy, że nasz plik z mapowaniem nazywał się Dane. Dzięki Linq stworzy nam się klasa która nazwana zostanie (w tym wypadku) DaneDataContext. Dzięki niej będziemy mogli odwoływać się do obiektów stworzonych przez LINQ.

Zapytania w linq, tworzenie nowych danych

Aby wyciągnąć dane z tabeli za pomocą linq tworzymy najpierw nowy obiekt DataContext, o którym wspominałem w poprzednim akapicie a następnie za pomocą zapytania podobnego do sql wyciągamy dane. Poniższy przykład pokazuje jak to osiągnąć:

   1: using (var zalacznik = new DaneLinqDataContext())
   2:              
   3:                  var zatwierdz = from zat in zalacznik.Zalaczniks where zat.NewsId == null select zat;
   4:        }

Przykład dodania danych prezentuje się następująco:

   1: using (var DodajNews = new DaneDataContext())
   2:             {
   3:  
   4:                 New news = DodajNews.News.Single(n => n.Id == id);
   5:                 news.Kategoria = int.Parse(KategorieDrop.SelectedValue);
   6:                 news.Tytul = TytulNewsBox.Text;
   7:                 news.SkroconyOpis = SkroconyOpisBox.Text;
   8:                 news.Tresc = PoleWpis.Content;
   9:                 news.Piorytet = int.Parse(PriorytetBox.SelectedValue);
  10:                 news.Waga = int.Parse(WagaBox.SelectedValue);
  11:                 news.DataUtworzenia = DateTime.Now;
  12:                 DodajNews.SubmitChanges();
  13:                          
  14:  
  15:             }

Widzimy, że wystarczy stworzyć nowy obiekt linq a następnie ustawiać właściwości danej klasy i na końcu je zapisać.

 

To wszystko w dzisiejszym artykule. Jak widzicie zakres materiału jest bardzo duży. W samym training kicie było ok 50 stron. Warto poczytać dużo więcej o powyższych zagadnieniach ponieważ przedstawione przeze mnie są tylko podstawowe informację. Nie pisałem również o asynchronicznym dostępnie do danych.
Wybaczcie literówki ale jest już troszkę późno a nie wiem czy jutro zdążę sprawdzić :)

Tagi: , , , , ,