1 LINQ 1
1. Cel zajęć Celem zajęć jest zapoznanie się z technologią LINQ oraz tworzeniem trójwarstwowej aplikacji internetowej. 2. Zadanie Proszę przygotować aplikację WWW, która: będzie pozwalała na generowanie prostych artykułów podzielonych na kategorie. W aplikacji mają być wyraźnie wydzielone warstwy: o dostępu do danych (odpowiadająca za komunikację z bazą danych) o biznesowej (odpowiadającej za wykonanie reguł biznesowych w aplikacji) o prezentacji (odpowiadającej za komunikację z użytkownikiem) komunikacja z bazą danych ma się odbywać za pomocą technologii Linq To SQL W tym celu należy wykonać odpowiednie kroki: I. Stworzenie bazy danych a) Tworzymy nową aplikację typu ASP.NET Web Application (jako nazwę należy przyjąć Zadanie 1.1) b) W celu utworzenia nowej bazy danych w oknie Solution Explorer klikamy prawym przyciskiem myszy na katalogu App_Data i z menu kontekstowego wybieramy opcję Add > New Item. c) W wyświetlonym oknie dialogowym wybieramy opcję SQL Server Database, a jako nazwę pliku podajemy Artykuly.mdf i wciskamy przycisk Add. 2
d) Ponownie otwieramy okno Solution Explorer i klikamy dwukrotnie na nowej pozycji Artykuly.mdf w celu otwarcia okna edycji bazy danych. e) Klikając prawym przyciskiem myszy na węźle Tables i wybieramy opcję Add New Table. Wyświetlone zostanie okno edycji tabeli, które wypełniamy tak jak pokazano na rysunku poniżej Proszę pamiętać o ustawieniu klucza głównego tabeli na polu ID. f) Podobnie tworzymy drugą tabelę Artykuly i ustawiamy w niej następujące pola g) Połączmy te dwie tabele relację Jeden do wielu. h) Zapiszmy wprowadzone zmiany. II. Stworzenie warstwy dostępu do danych a) W oknie SolutionExplorer tworzymy nowy katalog DAL (Data Access Layer) i wszystkie pliki, o których będzie mowa w tym punkcie będziemy umieszczać w tym katalogu. b) Ponownie otwórzmy okno SolutionExplorer i klikając prawym przyciskiem myszy wybieramy opcję Add > New Item. c) Z grupy Data wybieramy opcję LINQ to SQL Classes, jako nazwę podajemy Artykuly.dbml i klikamy przycisk Add. 3
Stanowi to podstawę do utworzenia mapowania obiektowo relacyjnego umożliwiającego wykonywanie zapytań LINQ to SQL. d) Otwieramy okno Server Explorer i przeciągamy z niego obie utworzone w bazie danych tabele, które umieszczamy w otwartym oknie projektanta mapowania obiektowo relacyjnego. W efekcie w oknie tym powinny się pojawić następujące elementy: Należy zauważyć, że VisualStudio automatycznie rozpoznało połączenie pomiędzy odpowiednimi tabelami. e) Ponownie otwórzmy okno SolutionExplorer i dodajmy do projektu nowy interfejs (Add > New Item > Code > Interface), który nazwiemy IArtykuly. Interfejsu wygląda następująco: public interface IArtykuly List<Artykul> PobierzArtykulyZKategorii( int Kategoria); Artykul PobierzArtykul(int ID); void DodajArtykul(string tytul, string tresc, int Kategoria); void EdytujArtykul(string tytul, string tresc, int Kategoria, int ID); void UsunArtykul(int ID); f) Następnie, analogicznie, tworzymy drugi interfejs IKategorie: public interface IKategorie List<Kategoria> PobierzWszystkie(); Kategoria PobierzKategorieWgID(intint ID); void DodajKategorie(string Nazwa, string Opis); void EdytujKategorie(string Nazwa, string Opis, int ID); void UsunKategorie(int ID); g) Kolejno tworzymy dwie klasy pomocnicze: 4
public class Kategoria public int ID get; set; public string Nazwa get; set; public string Opis get; set; public int LiczbaArtykulow get; set; public class Artykul public int ID get; set; public string Tytul get; set; public string Tresc get; set; public int Kategoria get; set; public DateTime DataOstatniejModyfikacji get; set; public DateTime DataUtworzenia get; set; Elementy te będą służyły do komunikacji pomiędzy poszczególnymi warstwami h) Następnie tworzymy klasę która będzie realizowała interfejs IArtykul Klasa ta będzie odpowiedzialna za komunikację z bazą danych. public class ArtykulLINQ_To_SQL : IArtykuly public List<Artykul> PobierzArtykulyZKategorii( int Kategoria) using (Zadanie_1_1DataContext context = new Zadanie_1_1DataContext()) ❶ var artykuly = from a in context.artykulies where a.kategoria == Kategoria select new Artykul Tytul = a.tytul, ID = a.artykulid ; return artykuly.tolist(); ❷ ❸ 5
public Artykul PobierzArtykul(int ID) using (Zadanie_1_1DataContext context = new Zadanie_1_1DataContext()) try var artykul = context.artykulies.single( a => a.artykulid == ID); return new Artykul Tytul = artykul.tytul, Tresc = artykul.tresc, DataUtworzenia = artykul.datautworzenia, DataOstatniejModyfikacji = artykul.dataostatniejmodyfikacji, Kategoria = artykul.kategoria, ID = artykul.artykulid ; catch return null; public void DodajArtykul(string tytul, string tresc, int kategoria) using (Zadanie_1_1DataContext context = new Zadanie_1_1DataContext()) Artykuly art = new Artykuly Kategoria = kategoria, Tytul = tytul, Tresc = tresc, DataOstatniejModyfikacji = DateTime.Now, DataUtworzenia = DateTime.Now ; context.artykulies.insertonsubmit(art); context.submitchanges(); 6
public void EdytujArtykul(string tytul, string tresc, int kategoria, int ID) using (Zadanie_1_1DataContext context = new Zadanie_1_1DataContext()) var artykul = context.artykulies.single( a => a.artykulid == ID); artykul.tytul = tytul; artykul.tresc = tresc; artykul.kategoria = kategoria; artykul.dataostatniejmodyfikacji = DateTime.Now; context.submitchanges(); public void UsunArtykul(int ID) ❶ ❷ ❸ using (Zadanie_1_1DataContext context = new Zadanie_1_1DataContext()) var artykul = context.artykulies.single( a => a.artykulid == ID); context.artykulies.deleteonsubmit(artykul); context.submitchanges(); Utworzenie klasy kontekstu. Instrukcja ta zawarta jest w bloku using w celu automatycznego zwolnienia zasobów w momencie, gdy nie są już potrzebne Przykład typowego zapytania LINQ to SQL realizującego zarówno selekcję wierszy i projekcję kolumn z tabeli bazy danych. Ponieważ instrukcja w linii oznaczonej jako ❷ tworzy obiekt klasy IEnumerable<Artykul>, konieczne jest wywołanie funkcji ToList() w celu wykonania zapytania i wygenerowania wymaganej listy obiektów. i) Kolejno proszę utworzyć klasę KategoriaLINQ_To_SQL (na obraz i podobieństwo klasy ArtykulLINQ_To_SQL) 7
III. Stworzenie warstwy biznesowej a) Otwieramy okno SolutionExplorer i dodajmy do projektu (do katalogu głównego) dwie nowe klasy KategorieBLL i ArtykulyBLL. Klasy te będą stanowiły naszą warstwę biznesową. Warstwa ta powinna zawierać całą logikę aplikacji (sprawdzanie poprawności danych, autoryzację itd.) [DataObject] public class KategorieBLL : IKategorie IKategorie _kategorie; ❶ public KategorieBLL() : this(new KategorieLINQ_To_SQL()) ❷ public KategorieBLL(IKategorie kategoriedal) _kategorie = kategoriedal; [DataObjectMethod(DataObjectMethodType.Select, true)] ❸ public List<Kategoria> PobierzWszystkie() return _kategorie.pobierzwszystkie(); [DataObjectMethod(DataObjectMethodType.Select, false)] public Kategoria PobierzKategorieWgID(intint ID) return _kategorie.pobierzkategoriewgid(id); [DataObjectMethod(DataObjectMethodType.Insert, true)] public void DodajKategorie(string Nazwa, string Opis) _kategorie.dodajkategorie(nazwa, Opis); [DataObjectMethod(DataObjectMethodType.Update, true)] public void EdytujKategorie(string Nazwa, string Opis, int ID) _kategorie.edytujkategorie(nazwa, Opis, ID); [DataObjectMethod(DataObjectMethodType.Delete, true)] public void UsunKategorie(int ID) _kategorie.usunkategorie(id); 8
W przedstawianej tutaj prostej aplikacji klasa ta stanowi jedynie implementację interfejsu IKategorie. Klasa i jej metody opisane są odpowiednimi atrybutami, które pozwalają na łatwiejsze podłączenie jej z obiektami typu ObjectDataSet znajdującymi się w warstwie prezentacji. ❶ Atrybut ten informuje Visual Studio, że klasa ta jest odpowiedzialna za dostarczenie danych dla warstwy prezentacji. ❷ Klasa zawiera dwa konstruktory. Pierwszy domyślny wymagany jest przez Visual Studio, drugi umożliwia wstrzyknięcie obiektu umożliwiającego połączenia z bazą danych. ❸ Atrybut opisujący metody DataObjectMethod pozwala na określenie jej przeznaczenia. Składnia tego atrybutu jest następująca: DataObjectMethod(DataObjectMethodType, bool) Pierwszy parametr określa przeznaczenie metody, drugi czy jest to metoda domyślna czy nie. b) Proszę stworzyć klasę stanowiącą warstwę biznesową dla tabeli Artykuły. c) Proszę przebudować projekt (Build > Build Solution). IV. Stworzenie warstwy prezentacji a) W stronie master page proszę zmienić komponent menu tak aby wskazywał na dwie strony: kategorie.aspx i artykuly.aspx b) Proszę dołączyć do projektu nową stronę kategorie.aspx c) Na stronie tej proszę umieścić następujące komponenty i nadać im nazwy podane w nawiasach: LinkButton (NowaKategoria) DetailsView (KategoriaDV) GridView (KategorieGV) ObjectDataSource (KategoriaODS) ObjectDataSource (KategorieODS) 9
d) Klikamy na pierwszy komponent typu ObjectDataSource i rozwijamy okienko Tasks, z którego wybieramy opcję Configure Data Source e) W oknie dialogowym wybieramy klasę, która będzie źródłem danych dla naszego obiektu. Jeżeli zaznaczymy opcję Show only data components powinny pojawić się tylko te klasy, które oznaczyliśmy atrybutem [DataObject]. Wybieramy klasę KategorieBLL i klikamy przycisk Next. 10
f) Następnie możemy ustawić z jakich metod wybranej wcześniej klasy ma korzystać źródło danych przy wykonywaniu operacji pobierania i edycji danych. Zauważmy że odpowiednie metody zostały już wybrane dzięki ustawionym wcześniej atrybutom. Zmieńmy jednak metodę w zakładce Select na PobierzKategorieWgID, pozostałe wpisy pozostawmy bez zmian. Klikamy przycisk Next. g) Ponieważ wybrana metoda PobierzKategorieWgID ma przypisany parametr w następnym oknie jesteśmy proszeni o wybranie źródła wartości dla niego. W naszym przypadku wybieramy wartości tak jak pokazano na rysunku poniżej (przy założeniu, że komponent GridView nazwany jest KategorieGV). 11
h) Klikamy przycisk Finish. i) Następnie klikamy raz jeszcze na modyfikowanym w poprzednich krokach obiekcie i w oknie Properties zmieniamy wartość właściwości OldValuesParameterFormatString z orginal_0 na 0. j) Następnie klikamy na obiekcie typu DetailsView, rozwijamy okienko Task, w którym ustawiamy opcje tak jak pokazano na rysunku poniżej i klikamy opcje EditFields. ds. k) W wyświetlonym oknie dialogowym w okienku: Selected Fields usuwamy pozycje tak aby pozostały tylko: Nazwa, Opis, CommandField. Następnie klikamy na CommandField i w oknie CommandField properties odnajdujemy pozycję NewText, z której usuwamy zawartość. 12
l) Klikamy na przycisk OK. w celu zamknięcia okna. m) W oknie Properties dla pozycji DataKeyNames przypisujemy wartość ID. n) Następnie w podobny sposób proszę skonfigurować drugi obiekt typu ObjectDataSet, przy czym w zakładce Select proszę wybrać metodę PobierzWszystkie. o) Obiekt GridView proszę podłączyć do drugiego obiektu typu ObjectDataSet i skonfigurować tak aby możliwe było wybranie opcji Edit lub Delete. Proszę również ustawić właściwość DataKeyNames na wartość ID p) Następnie proszę przypisanie do odpowiednich komponentów następujących metod obsługi zdarzeń: LinkButton (NowaKategoria) Click protected void NowaKategoria_Click( object sender, EventArgs e) KategoriaDV.ChangeMode(DetailsViewMode.Insert); KategoriaDV.Visible = true; Metoda ta ma za zadanie wyświetlić komponent DetailsView i przełączyć go automatycznie w tryb wprowadzania nowych danych. GridView (KategorieGV) RowEditing protected void KategorieGV_RowEditing( object sender, GridViewEditEventArgs e) KategorieGV.SelectedIndex = e.neweditindex; KategoriaDV.ChangeMode(DetailsViewMode.Edit); KategoriaDV.Visible = true; e.cancel = true; Metoda ta ma za zadanie wyświetlić komponent DetailsView i przełączyć go automatycznie w tryb wprowadzania edycji danych dla wiersza wybranego w GridView 13
DetailsView (KategoriaDV) ModeChanging protected void KategoriaDV_ModeChanging( object sender, DetailsViewModeEventArgs e) if (e.newmode == DetailsViewMode.ReadOnly) KategoriaDV.Visible = false; Metoda ta ma za zadanie ukryć komponent DetailsView, w momencie gdy użytkownik zatwierdzi lub anuluje edycję danych. q) Następnie proszę o przygotowanie drugiej strony Artykuly.aspx tak aby wyglądała mniej więcej jak na rysunku poniżej: Komponent DropDownList (Kategoria) ma pozwolić na wybranie kategorii, z której mają być wyświetlone artykuły. Pozostałe komponenty powinny się zachowywać mniej więcej tak jak na poprzedniej stronie. Mała podpowiedź: Ponieważ w komponencie DetailsView ma nie być pozycji Kategoria, jej wartość musi być pobierana z komponentu DropDownList. Aby wartość ta była dołączona do zestawu danych przekazanego do warstwy biznesowej konieczne jest przypisanie do komponentu DetailsView dwóch metod obsługi zdarzeń: ItemInserting oraz ItemUpdating, w których wpisujemy odpowiednio: e.values["kategoria"] = KategorieDDL.SelectedValue; e.newvalues["kategoria"] = KategorieDDL.SelectedValue; 14
V. Zadania do samodzielnego wykonania Jeżeli wystarczy Państwu czasu to na zajęciach, jeżeli nie to w domu proszę w ramach powtórzenia o rozbudowanie tej aplikacji o technologię asunchronicznego przesyłu danych AJAX oraz dołączenie do niej strony, która umożliwiła by przeglądanie artykułów w trybie tylko do odczytu. 15