Inżynieria Programowania - Lab3

Transkrypt

Inżynieria Programowania - Lab3
Inżynieria Programowania
Laboratorium 4
Biblioteka Połaczeniowa
Paweł Paduch [email protected]
20-04-2013
Rozdział 1
Wstęp
Na dzisiejszych zajęciach zajmiemy się tworzeniem biblioteki kodu obsługującego bazę danych. Zrealizujemy podstawową funkcjonalność, dodawania, wyszukiwania i edycji wybranych danych. Postaramy się zrealizować jedną z trudniejszych rzeczy, mianowicie dodawanie propozycji rezerwacji z jednoczesnym
sprawdzaniem czy nie koliduje z istniejącymi rezerwacjami. Do implementacji
posłuży nam środowisko Visual Studio 2010, platformą bazodanową będzie SQL
Server 2008. Posłużymy się modelem programowania ADO.NET Entity Framework. W krótkim wyjaśnieniu czym jest Entity Framework (EF) posłużę się
cytatem z książki A. Troelsena [1].
„Nadrzędnym celem EF jest umożliwienie komunikacji z relacyjnymi
bazami danych za pomocą modelu obiektowego skojarzonego bezpośrednio z biznesowymi obiektami aplikacji. Nie musimy na przykład
traktować porcji danych jako kolekcji wierszy i kolumn, ale możemy
operować na kolekcji obiektów ze ścisłą kontrola typów, nazywanych
encjami.”
Dzięki kompatybilności obiektów encji z LINQ można stosować do nich ten
język zapytań. Zapytanie LINQ jest tłumaczone przez silnik wykonawczy EF
na zapytanie SQL.
Oprócz wspomnianej wcześniej książki, pomocne jak zwykle będą strony z
dokumentacją. Poniżej ogólnie o bazach danych i platformie .Net 4.0
http://msdn.microsoft.com/en-us/library/vstudio/951h6we4(v=vs.100).aspx
A tu konkretnie o modelu Entity Framework.
http://msdn.microsoft.com/en-us/library/vstudio/bb399572(v=vs.100).aspx
1
Rozdział 2
Jeszcze drobne poprawki w
bazie
Ostatnio zapomnieliśmy o tym, że login osoby powinien być unikalny. Aby
zapewnić unikalność z poziomu bazy danych należy, założyć indeks na polu Login w tabelce Osoby.
W Serwer Explorerze klikamy dwukrotnie tabelę Osoby w celu otworzenia jej do
edycji. Z menu kontekstowego tabelki wybieramy Indexes/Keys. Klikamy guzik
Add i przechodzimy do edycj nowo powstałego indeksu. W polu Columns wybieramy nazwę naszej kolumny Login a w polu Type wybieramy Unique Key. (rys.
2.1) Można też ustawić nazwę na Index Login. Po zmianach należy je zapisać.
Rysunek 2.1: Indeksy i klucze
Jak się później można było by przekonać, używanie typu nchar niesie ze sobą
pewne zalety ale też i wady. Zaletą jest, lepsza wydajność, nie trzeba alokować
pamięci bo typ ten na stałe bierze zadaną liczbę znaków. Wada objawia się przy
2
wyświetlaniu. Do wartości zawartych w typie nchar dokładane są spacje. można
oczywiście korygować to wywołując metodę Trim jednak przy każdym użyciu
robi się to uciążliwe. Stąd decyzja o zamianie wszystkich typów nchar na typy
varchar. Maksymalna długość pozostanie taka sama.
3
Rozdział 3
Biblioteka dostępu do bazy
Aby umożliwić wielokrotne wykorzystanie kodu przez różne aplikacje (lub
różne wersje tej samej aplikacji, np. ASP.NET, WPF) stworzymy osobną bibliotekę dzięki której ułatwimy sobie dostęp do naszej bazy danych.
3.1
Nowy projekt
Zakładamy nowy projekt podając jak typ projektu ClassLibrary i zmieniamy nazwę na RezerwacjeLib. Jest to ważne, ponieważ nazwa ta będzie też
domyślną przestrzenią nazw.
Do projektu dodajemy (z menu kontekstowego projektu Add→New Item) ADO.NET
Entity Data Model nazywając go Rezerwacje.edmx (rys. 3.1).
Rysunek 3.1: Dodanie EDM Rezerwacje
Wybieramy Generate From Database (jeżeli nie jesteśmy połączeni należy
się wcześniej połączyć), na następnych oknach kreatora wybieramy połączenie
z naszą bazą danych oraz zaznaczamy opcję Save entity connection setings in
4
App.config as podając nazwę rezerwacjeEntities (rys.3.2). Zaznaczamy, że chce-
Rysunek 3.2: Dodawanie EDM połaczenie
my zaimportować tabele oraz ustawiamy nazwę Model namespace na RezerwacjeModel (rys. 3.3).
3.2
Klasa Role
Stwórzmy sobie klasę Role do zarządzania uprawnieniami. Możliwe, że nazwa Uprawnienia bardziej by nam się kojarzyła, jednak jest ona już użyta w
przestrzeni nazw RezerwacjeEntity. Możnab y też wykorzystać fakt, że wygenerowane klasy w RezerwacjeEntity są częściowe (partial) i rozszerzyć ich funkcjonalność, ale na chwilę obecną stworzymy sobie swoje dodatkowe klasy.
Z menu kontekstowego projektu wybieramy Add→New Item→Class ustawiamy
nazwę na Role.cs. W klasie tej dodajemy nową przestrzeń nazw dopisując na
początku pliku using System.Data;. Do klasy dodać należy też obiekt kontekstu
encji rezerwacjeEntities context = new rezerwacjeEntities();. Tworzymy sobie 3
podstawowe metody znajdzUprawnieniePoNazwie, dodajUprawnienie oraz modyfikujUprawnienie. Metoda usunUprawnienie nie będzie nam potrzebna, gdyż
w działającej bazie danych nie przewidujemy takiej opcji. Kod 3.1 pokazuje nam
przykładową implementację.
5
Rysunek 3.3: Dodawanie EDM połaczenie
6
Listing 3.1: Klasa Role.cs
1
2
3
4
5
using
using
using
using
using
System;
System.Collections.Generic;
System.Linq;
System.Text;
System.Data;
6
7
8
9
10
11
namespace RezerwacjeLib
{
public class Role
{
rezerwacjeEntities context = new rezerwacjeEntities();
12
13
14
15
16
17
18
19
20
21
/// <summary>
/// Wyszukaj uprawnienie po jego nazwie
/// </summary>
/// <param name="nazwa">Nazwa uprawnienia</param>
/// <returns>Jeżeli istnieje to zwróci znalezione uprawnienie</returns>
public Uprawnienia znajdzUprawnieniePoNazwie(string nazwa)
{
return context.Uprawnienia.Where("it.Nazwa='" + nazwa + "'").←FirstOrDefault();
}
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
/// <summary>
/// Dodaje nowe uprawnienie
/// </summary>
/// <param name="nazwa">Krótka nazwa nowego uprawnienia</param>
/// <param name="opis">Dodatkowy ewentualny opis</param>
/// <returns>Zwraca nowo dodane uprawnienie lub null w przypadku ←niepowodzenia</returns>
public Uprawnienia dodajUprawnienie(string nazwa,string opis)
{
Uprawnienia u = new Uprawnienia() { Nazwa = nazwa, Opis = opis};
context.Uprawnienia.AddObject(u);
try
{
//zapisz wszystkie zmiany w fizycznej bazie
//i zresetuj śledzenie zmian w obiekcie kontekstu
context.SaveChanges();
}
catch (Exception ex)
{
//nie udalo sie wstawic wiec usuwamy
context.Uprawnienia.DeleteObject(u);
Console.WriteLine("Nie udało się dodać nowego uprawnienia: "+ex.←Message);
Console.WriteLine(ex.InnerException.Message);
return null;
}
Console.WriteLine("Dodano nowe uprawnienie: id={0} {1} {2}", u.←IdUprawnienia, u.Nazwa, u.Opis);
return u;
}
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
/// <summary>
/// Modyfikacja uprawniania należy wyszukać uprawnienie zmienić mu pola i ←wywołać tę metodę podając zmienione uprawnienie jako parametr.
/// Identyfikatora nie ruszać!
/// </summary>
/// <param name="upr">Uprawnienie ze zmienionymi nowymi polami</param>
/// <returns></returns>
public bool modyfikujUprawnienie(Uprawnienia upr)
{
EntityKey key = new EntityKey("rezerwacjeEntities.Uprawnienia", "←IdUprawnienia", upr.IdUprawnienia);
Uprawnienia u = (Uprawnienia)context.GetObjectByKey(key);
if (u != null)
{
u.Nazwa = upr.Nazwa;
u.Opis = upr.Opis;
try
{
7
67
68
69
70
71
72
Message);
73
74
75
76
77
78
79
80
81
82
83
}
}
context.SaveChanges();
}
catch (Exception ex)
{
//nie udalo sie
Console.WriteLine("Nie udało się zmienić uprawnienia: " + ex.←Console.WriteLine(ex.InnerException.Message);
return false;
}
Console.WriteLine("Zmieniono uprawnienie o id={0} na {1} {2}", u.←IdUprawnienia, u.Nazwa, u.Opis);
return true;
}
Console.WriteLine("Nie znaleziono uprawnienia do modyfikacji");
return false;
}
8
Rozdział 4
Testowanie nowej biblioteki
W celu sprawdzenia czy powyższe metody w klasie Role działają napiszemy
szybko bardzo prostą aplikację konsolową. W tym celu otwieramy sobie nowe Visual Studio (tak będziemy mieć otwarte oba programy jednocześnie), tworzymy
nowy projekt typu Console Application. Podłączamy bibliotekę poprzez dodanie referencji do niej. Z menu kontekstowego projektu wybieramy Add Reference
klikamy na zakładkę Browse i nawigujemy do katalogu w którym znajduje się
projekt RezerwacjeLib. Wchodzimy do katalogu RezerwacjeLib/obj/Debug i wybieramy plik RezerwacjeLib.dll (rys. 4.1). Jeżeli pliku nie znajdziemy to znaczy,
że nie przekompilowaliśmy biblioteki (klawisz F6).
Należy też dodać referencję do System.Data.Entity tylko z zakładki .Net.
Żeby nasza aplikacja łączyła się z tą samą bazą do której stworzona jest biblioteka RezerwacjeLib należy jeszcze do naszego projektu dodać plik konfiguracji
App.config. Jest tam zapisany łańcuch połączenia. Robimy to za pomocą menu
kontekstowego projektu Add→Existing Item i nawigujemy do katalogu z projektem RezerwacjeLib w nim znajdziemy plik App.config (należy filtr plików
ustawić na wszystkie pliki *.*).
Uzupełniamy kod jak w listingu 4.1.
Listing 4.1: Program konsolowy operujący na upawnieniach
1
2
3
4
5
using
using
using
using
using
System;
System.Collections.Generic;
System.Linq;
System.Text;
RezerwacjeLib;
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
//Dodawanie ról/uprawnień
Role role = new Role();
Uprawnienia upr;
if ((upr = role.dodajUprawnienie("Inny", "Testowe uprawnienie bez ←znaczenia strategicznego")) != null)
Console.WriteLine("ok dodano");
else
return;
upr = role.znajdzUprawnieniePoNazwie("Inny");
if (upr != null)
9
Rysunek 4.1: Dodawanie referencji
10
{
22
23
24
25
26
27
28
29
30
}
}
Console.WriteLine("ok znaleziono {0} a opis jest taki: {1} ",upr.←Nazwa, upr.Opis);
upr.Nazwa = "Cocisk";
role.modyfikujUprawnienie(upr);
}
Console.ReadLine();
}
W linii 5 oprócz standardowych przestrzeni nazw można dodać RezerwacjeLib.
W linii 14 tworzymy obiekt klasy Role.
W linii 15 deklarujemy obiekt upr klasy Uprawnienia zdefiniowanej jako klasa
encji w pliku Rezerwacje.Designer.cs. Plik ten zosatł wygenerowany automatycznie, ale można go z ciekawości podejrzeć.
Następnie wywołujemy metodę dodajUprawnienie sprawdzając czy dostaliśmy
nowo stworzony obiekt.
W linii 20 testujemy metodę szukającą, następnie znaleziony obiekt modyfikujemy a zmiany utrwalamy metodą modyfikujUprawnienie w lini 25. Gdy wszystko
zadziałało możemy przejść do zakładki Server Explorer i podejrzeć dane w tabeli Uprawnienia klikając na jej nazwie prawym klawiszem myszy i wybierając z
menu kontekstowego Show Table Data. Teraz możemy napisać kawałek programu, który doda faktyczne dane takie jak Administrator, Superuser, Wykładowca
i Starosta. Należy pamiętać, że ewentualne zmiany w bibliotece pojawią się dopiero gdy ją przebudujemy (klawisz F6).
Kilka ogólnych uwag:
• Kody, które są tu umieszczone są wygenerowane do utworzonego wzorca
bazy, który podawałem na poprzednich zajęciach. Jeżeli ktoś odszedł od
podanego nazewnictwa otrzyma też inne klasy wygenerowane przez EF.
Przykładowo: wielkość liter ma znaczenie idOsoby to nie to samo co IdOsoby o takie błędy łatwo, część z nich będzie na etapie kompilacji, jednak
część z nich wyjdzie dopiero w momencie uruchomienia i np. odwołania
się do bazy danych.
• pamiętajmy by załączać wszystkie referencje i używać przestrzeni nazw
podanych w instrukcji.
• korzystajmy z Intellisense (inteligentego podpowiadacza) jeżeli edytor nie
„widzi” lub „widzi” inaczej klasy lub składowe naszych klas to znaczy, że
mogliśmy się pomylić w nazewnictwie lub przestrzeni nazw.
• Ponieważ kod biblioteki „obrabiamy” w innej instancji Visual Studio a
aplikację tworzymy w innej należy zwrócić uwagę w którym miejscu się
znajdujemy. Szczególnie łatwo się pomylić gdy po uruchomieniu programu wystąpił błąd w bibliotece wtedy debuger otworzy nam odpowiedni
plik *.cs w edytorze w którym uruchomiliśmy program. Nie należy go tam
jednak edytować a jeśli już zapędziliśmy się i szkoda nam zmian należy
wrócić do VS w którym edytujemy pliki biblioteki, zostaniemy poinformowani o tym że plik został zmieniony poza tym edytorem i czy załadować
go ponownie, trzeba się zgodzić i kontynuować we właściwym miejscu.
11
• Jeżli wprowadzamy jakieś zmiany w pliku biblioteki np. Role.cs należy
skompilować bibliotekę (F6) i dopiero wtedy przejść do kompilacji programu.
• W przykładowych programach często brak jest przechwytywania wyjątków. Po pierwsze upraszczamy przedstawiony kod. Po drugie w razie awarii
w trybie debugowania możemy sobie przejrzeć pełną informację. Oczywiście docelowo należy się zabezpieczyć na wszelkie ewentualności.
• W przykładowych programach każda klasa powinna zaimplementować interfejs IDisposable by w przypadku usuwania zwolnić zapisać zmiany i
zwolnić kontekst tak jak w przykładowym kodzie 4.2.
• Podczas tworzenia nowych klas zwróćmy uwagę by były one dostępne,
kwalifikator dostępu public. Domyślnie w C# klasy są prywatne.
• Gdy kopiujemy kod z instrukcji wstawiają się numerki i usuwane jest formatowanie, trzeba też zwrócić uwagę na zawinięte linijki gdzie w miejscu
złamania dorzucona została spacja i minus. Formatowanie można wykonać w następujący sposób. Zaznaczyć kod (ctrl+a) przejść w tryb edycji
(ctrl+e) sformatować f. Tryb edycji pozwala też zakomentować zaznaczony tekst (literka c) lub odkomentować (literka u).
Listing 4.2: Eleganckie kończenie
1
2
3
4
5
public void IDisposable()
{
context.SaveChanges();
context.Dispose();
}
• Pamiętajmy, że po zmianach w bazie należy zaktualizować EDMX.
12
Rozdział 5
Rozszerzamy bibliotekę
Teraz na podobnej zasadzie proszę napisać identyczną klasę obsługującą
Tytuły. Nazwiemy ją Przedrostki. Powinna mieć analogiczne metody jak klasa zarządzająca uprawnieniami. Należy ją przetestować, sprawdzając wyniki w
fizycznej bazie. Dodajmy od razu kilka tytułów takich jak: mgr, mgr inż., dr, dr
inż., dr hab., dr hab. inż.,prof. itp.
Możemy przejść do obsługi osób. Też należy stworzyć metody do dodawania, modyfikowania i wyszukiwania. Jednak tu musimy uwzględnić dodatkowe
pola jak klucze obce. Chcielibyśmy też mieć możliwość szukania po nazwisku,
imieniu, nazwisku i imieniu albo po fragmencie nazwiska.
Tworzymy nową klasę Uzytkownicy.cs i w niej umieszczamy odpowiedni kod.
W listingu 5.1 mamy metodę dodającą nową osobę.
Listing 5.1: Metoda dodająca nową osobę
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public Osoby wstawOsobe(string imie, string nazwisko, string login, string haslo, ←string mail, string opis, Tytuly tytul, Uprawnienia uprawnienie)
{
Osoby os = new Osoby() { Imie = imie, Nazwisko = nazwisko, Login = login, Haslo =←haslo, Mail = mail, Opis = opis, IdTytulu = tytul.IdTytulu, IdUprawnienia = ←uprawnienie.IdUprawnienia };
context.Osoby.AddObject(os);
try
{
context.SaveChanges();
}
catch (Exception ex)
{
//nie udalo sie wstawic wiec usuwamy
context.Osoby.DeleteObject(os);
Console.WriteLine("Nie udało się dodać nowej osoby: " + ex.Message);
Console.WriteLine(ex.InnerException.Message);
return null;
}
Console.WriteLine("Dodano nową osobę: id={0} {1} {2}", os.IdOsoby, os.Imie, os.←Nazwisko);
return os;
}
W listingu 5.2 mamy metodę aktualizującą daną osobę. Należy ją tak przerobić aby zwracała typ bool oznaczający czy metoda się powiodła. Oraz wypisywała dane diagnostyczne na konsolę. Analogicznie jak robi to metoda modyfikujUprawnienie.
13
Listing 5.2: Metoda modyfikująca wybraną osobę
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public void aktualizujOsobe(Osoby osoba)
{
//najpierw szukamy odpowiedniej osoby po id
EntityKey key = new EntityKey("rezerwacjeEntities.Osoby", "IdOsoby", osoba.←IdOsoby);
Osoby os = (Osoby)context.GetObjectByKey(key);
if (os != null) //jeżeli mamy ją to aktualizujemy
{
os.Imie = osoba.Imie;
os.Nazwisko = osoba.Nazwisko;
os.Login = osoba.Login;
os.Haslo = osoba.Haslo;
os.Mail = osoba.Mail;
os.IdTytulu = osoba.IdTytulu;
os.IdUprawnienia = osoba.IdUprawnienia;
os.Opis = osoba.Opis;
context.SaveChanges();
}
}
W kolejnej metodzie 5.3 widzimy przykład szukania z wykorzystaniem zapytań LINQ. Zwrotnie chcemy dostać listę osób, których nazwiska pasują do
jednego ze wzorców (zaczyna się na, kończy na, zawiera, jest równe).
Listing 5.3: Szukanie osób po nazwisku
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public List<Osoby> znajdzOsobyPoNazwisku(string nazwisko)
{
string bezGwiazdek = nazwisko.Replace("*", "");
if (nazwisko.StartsWith("*"))
{
if (nazwisko.EndsWith("*")) //jeżeli zaczyna się i kończy gwiazdką to znajdź ←zawierające
return (from os in context.Osoby where os.Nazwisko.Contains(bezGwiazdek) ←select os).ToList();
//gdy tylko zaczyna się gwaizdką to znajdź kończące się nazwiska na podane ←nazwisko
return (from os in context.Osoby where os.Nazwisko.Trim().EndsWith(←bezGwiazdek) select os).ToList();
}
if (nazwisko.EndsWith("*")) //gdy tylko kończy się gwiazdka to tylko zaczynające ←się na dane litery
{
return (from os in context.Osoby where os.Nazwisko.StartsWith(bezGwiazdek) ←select os).ToList();
}
//gdy brak gwiazdek chodzi nam o konkretne nazwisko
return (from os in context.Osoby where os.Nazwisko == nazwisko select os).ToList←();
}
Przetestujmy teraz nowe metody w programie konsolowym. Dodajemy następujący fragment kodu 5.4 w celu sprawdzenia czy da się dodać osoby.
Listing 5.4: Testowanie dodawania osób
1
2
3
4
5
6
7
//Dodawanie uzytkownikow
Uzytkownicy users = new Uzytkownicy();
Role rola = new Role();
try
{
users.wstawOsobe("Marian", "Wykładnik", "marianw", "maryjanek", "[email protected]"←, "testowo", Przedrostki.znajdzTytulPoNazwie("dr hab."),rola.←znajdzUprawnieniePoNazwie("Wykładowca"));
// users.wstawOsobe("Anna", "Kajak", "azygzak", "azygzak1", "[email protected]", "←testowo", Tytuly.CreateTytuly(3), Uprawnienia.CreateUprawnienia(3, "tester"));
14
8
9
10
11
12
}
catch (Exception ex)
{
Console.WriteLine("Blad wstawiania: " + ex.InnerException.Message);
}
Fragment kodu 5.5 pokazuje przykład użycia aktualizatora osób.
Listing 5.5: Testowanie aktualizacji osób
1
2
3
4
5
6
7
8
//aktualizuj osobe
Osoby os = users.znajdzOsobe(10L);
Console.WriteLine("Znalaziono: " + os.Tytuly.Tytul + " " + os.Imie.Trim() + " " + os.←Nazwisko.Trim());
os.Opis = "to jest nowy opis po aktualizacji";
os.Imie = "Leonardo";
os.Login = "leon";
Console.WriteLine("login os [" + os.Login + "]");
users.aktualizujOsobe(os);
We fragmencie kodu 5.6 mamy przykład jak wyszukać i wyświetlić wiele
osób.
Listing 5.6: Testowanie szukania osób
1
2
3
4
5
6
7
//szukanie osob i wyswietlanie ich
List<Osoby> osoby = users.znajdzOsobyPoNazwisku("*");
Console.WriteLine("znaleziono nastepujace osoby w liczbie " + osoby.Count + " :");
foreach (Osoby it in osoby)
{
Console.WriteLine("-> " + (it.Tytuly != null ? it.Tytuly.Tytul + " " : "") + it.←Imie + " " + it.Nazwisko);
}
Kolejnym zadaniem jest napisanie analogicznej funkcji do szukania osób po
imieniu a także po imieniu i nazwisku.
Metoda szukająca po loginie też jest ciekawa ponieważ zapytanie LINQ może
zwrócić całą kolekcję znalezionych obiektów a nam chodzi o jeden. Login jest
jeden więc podajemy konkretny login i oczekujemy konkretnego użytkownika.
Pomocna będzie metoda FirstOrDefault. Przkład jest w kodzie 5.7
Listing 5.7: Szukanie osób z danym loginem
1
2
3
4
5
public Osoby znajdzOsobePoLoginie(string login)
{
var osoby = (from os in context.Osoby where os.Login == login select os).←FirstOrDefault();
return osoby;
}
Jednym z ostatnich samodzielnych zadań na dziś będzie dodanie obsługi
zasobów. Jednak należy zostawić to już na sam koniec, gdy zostanie chwila
czasu. Na chwilę obecną należy dodać ręcznie ze 2-3 zasoby za pomocą Server
Explorera aby można było przetestować dalsze modyfikacje.
15
Rozdział 6
Dodawanie rezerwacji
Dodanie rezerwacji wiąże się z rozwiązaniem kilku problemów. Po pierwsze
trzeba sprawdzić czy w danym przedziale czasowym nie uczestniczy już zasób,
który jest na liście rezerwowanych aktualnie zasobów. Należy też uwzględnić,
przypadek spotkań kiedy to rezerwujemy komuś czas i także sprawdzić jego
zajętość w podanym terminie. Po drugie powinniśmy rozwiązać problem współbieżności. Można to zrobić np. tworząc procedurę składowaną, którą wykonywalibyśmy w transakcji. Aby użyć klasy TransactionScope należy dodać do referencji System.Transactions. My jednak posługujemy się EF i za jego pomocą
postaramy się rozwiązać ten problem.
W normalnym scenariuszu odczytalibyśmy zawartość tabelek, sprawdzili warunki oraz dopisali lub zmienili zawartość tabelek. W kodzie przedstawiony w
listingu 6.1 przed wywołaniem SaveChanges można dodać sztuczne zatrzymanie
programu np. za pomocą Console.ReadLine. Uruchomić dwa programy z identycznymi parametrami rezerwacji i nacisnąć enter w jednym a potem w drugim.
Teraz to samo powtórzyć ale komentując kod odpowiedzialny za transakcje.
Listing 6.1: Dodawanie rezerwacji
1
2
3
4
public void dodajRezerwacje(long idDodajacego, DateTime dtStart, DateTime dtStop, ←List<long> idkiZasobow, List<long> idkiLudzikow, TypyRezerwacji typ)
{
using (TransactionScope scope = new TransactionScope())
{
5
6
7
8
9
10
11
12
13
14
15
16
17
//szukamy konfliktowych rezerwacji, czyli takich gdzie ktorys z zasobow (lub ←osob) jest zajety w podanych przedzialach czasowych
var konfliktoweRez = (from rz in context.Rezerwacja
from zas in context.RezerwacjaZasoby
from zasobyDoRezerwacji in idkiZasobow
from os in context.RezerwacjaOsoby
from osobyDoRezerwacji in idkiLudzikow
where ((rz.Od <= dtStart && rz.Do > dtStart) || (←dtStart <= rz.Od && dtStop > rz.Od)) //warunek czy podany przedzial czasowy nie←zalapuje innych rezerwacji z tymi samymi zasobami
&& rz.IdRezerwacji == zas.IdRezerwacji //←zlaczenie tabeli rezerwacji z tabela zawierajaca identyfikatory zasobow
&& zas.IdZasobu == zasobyDoRezerwacji //←uwzglednienie tylko zasobow ktore chcemy zarezerwowac
&& os.IdRezerwacji == rz.IdRezerwacji //i czy ←dane osoby tez nie sa zajete w tym czasie
&& os.IdOsoby == osobyDoRezerwacji
select rz).Distinct(); //Distinct by zwracalo unikalne ←rekordy rezerwacji (bo gdy rezerwacja ma np. dwa zasoby to wtedy zwracana jest ←-
16
dwa razy)
18
19
20
21
22
23
24
25
26
27
if (konfliktoweRez.Count() > 0) //jezeli sa jakies konflikty to wypisz i ←wyjdz. Moze pozwolimy jakos inaczej reagowac
{
Console.WriteLine("Znalazl konfliktowe rezerwacje w liczbe: " + ←konfliktoweRez.Count());
foreach (Rezerwacja r in konfliktoweRez)
{
Console.WriteLine("Rezerwacja {0} od: {1} do: {2}", r.IdRezerwacji, r←.Od, r.Do);
}
return;
}
28
29
30
31
32
//zakladamy propozycje
Propozycje propozycja = new Propozycje();
propozycja.IdOsoby = idDodajacego; //kto składa propozycje
context.AddToPropozycje(propozycja);
33
34
35
36
37
38
39
//dodajem rezerwacje okreslajac date i typ
Rezerwacja rezerwacja = new Rezerwacja();
rezerwacja.Od = dtStart;// DateTime.Parse(dataOdstr); //od kiedy
rezerwacja.Do = dtStop; // DateTime.Parse(dataDostr); //do kiedy
rezerwacja.IdTypuRez = typ.IdTypuRez; //jakiego typu rezerwacja
context.AddToRezerwacja(rezerwacja);
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
//wstawiamy laczniki do rezerwowanych zasobow przez dana rezerwacje
foreach (long idz in idkiZasobow)
{
RezerwacjaZasoby rezZas = new RezerwacjaZasoby();
rezZas.IdRezerwacji = rezerwacja.IdRezerwacji;
rezZas.IdZasobu = idz; // tutaj co rezerwujemy
context.AddToRezerwacjaZasoby(rezZas);
}
bool trzebaZgody = false;
//Rezerwujemy czas większej liczbie osób np. chcemy zorganizowac spotkanie
foreach (long ido in idkiLudzikow)
{
RezerwacjaOsoby rezOs = new RezerwacjaOsoby();
rezOs.IdOsoby = ido;
rezOs.IdRezerwacji = rezerwacja.IdRezerwacji;
if (rezOs.IdOsoby == idDodajacego) //jeżeli dodajemy sami siebie to z ←automatu jest zgoda
rezOs.Zgoda = true;
else
{
rezOs.Zgoda = false;
trzebaZgody = true;
}
context.AddToRezerwacjaOsoby(rezOs);
}
if (!trzebaZgody) //jeśli ja zakładam to nie trzeba zgody i można uaktywnić
{
rezerwacja.StanyRezerwacji = context.StanyRezerwacji.Where("it.Stan='←Aktywna'").FirstOrDefault(); //1 Aktywna
}
else //jesli sa inne osoby trzeba poczekac na ich zgode
{
rezerwacja.StanyRezerwacji = context.StanyRezerwacji.Where("it.Stan='Do ←zatwierdzenia'").FirstOrDefault(); // 4 do zatwierdzenia
}
//Console.WriteLine("Czekamy przed zapisem do bazy, klepnij enter");
//Console.ReadLine();
try
{
context.SaveChanges(); //ostatecznie zapisujemy zmiany, EF robi to w ←transakcji.
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
Console.WriteLine(ex.InnerException.Message);
return;
17
84
85
86
87
}
}
}
scope.Complete();//jezeli wszystko ok to zatwierdzamy transakcje
Do klasy Rezerwacje należy jeszcze dodać metodę znajdzTypRezerwacji gdzie
parametrem wejściowym będzie typ w postaci stringa.
Kod 6.2 przedstawia przykład użycia metody dodającej rezerwację.
Listing 6.2: Dodawanie rezerwacji przykład
1
2
3
4
5
6
7
Rezerwacje rez = new Rezerwacje();
//dodawanie rezerwacji
DateTime dtStart = DateTime.Parse("2013-04-20 20:00:00");
DateTime dtStop = DateTime.Parse("2013-04-20 21:00:00");
List<long> idZasobow = new List<long> { 3 };
List<long> idOsob = new List<long> { 5 };
rez.dodajRezerwacje(5L,dtStart,dtStop,idZasobow,idOsob,rez.znajdzRezerwacje("Egzamin←"));
Jeżeli data dodania automatycznie nam się nie wypełnia tylko ustawiana jest
np na 1900.01.01 należy napisać triggera wstawiającego bieżącą datę. Spowodowane jest to tym, że obiekty posiadające pola typu datetime automatycznie są
inicjowane a co za tym idzie INSERT do bazy ma podane pola z datą, więc
domyślna wartość getDate nie zadziała. W przypadku gdy chcemy usunąć rezerwacje należy pamiętać, że mamy relacje pomiędzy tabelami. Usuwając rekord
z Rezerwacji trzeba też usunąć wszystko co było podłączone. Najwygodniej jest
ustawić sobie usuwanie kaskadowe na relacjach pomiędzy tabelami. Wchodzimy w edycję tabeli Rezerwacje wybieramy z menu kontekstowego Relationship
i tam odszukujemy interesujące nas relacje. Rozwijając INSERT And UPDATE
Specyfication ustawiamy Delete Rule na Cascade (rys. 6.1).
Rysunek 6.1: Dodawanie referencji
18
Rozdział 7
Ocena
Za dzisiejsze laboratoria przewidywana jest następująca punktacja:
• Odtworzenie biblioteki z Rolami oraz poprawne użycie ich w programie
konsolowy 1 pkt. Wcześniej oczywiście należy założyć odpowiednie projekty i je skonfigurować przez dodanie referencji wygenerowanie edmx itd.
• Stworzenie własnej klasy Przedrostki z metodami które dodają, szukają
oraz modyfikują. Oczywiście nową klasę trzeba przetestować i zademonstrować w aplikacji konsolowej 1 pkt.
• Odtworzenie klasy Uzytkownicy oraz poprawne użycie w aplikacji konsolowej 0,5 pkt.
• Stworzenie własnej metody szukajPoImieniu wraz z przetestowaniem w
aplikacji 0,2 pkt.
• Stworzenie własnej metody szukajPoImieniuINazwisku wraz z przetestowaniem w aplikacji 0,3 pkt.
• Odtworzenie klasy Rezerwacje 0,2 pkt.
• Dodanie własnej metody znajdzTypRezerwacji 0,3 pkt.
• Dodanie triggera ustawiającego datę dodania 0,5 pkt.
• Dodanie obsługi „zasobów” analogicznie jak w klasie Role i Przedrostki 1
pkt.
Przez odtworzenie rozumiem przepisanie kodu z instrukcji, stworzenie czy dodanie jest pracą wykonaną samodzielnie jedynie na podstawie instrukcji. Pamiętajmy o terminowym wysłaniu sprawozdania, to też jest szansa na 1 punkt.
19
Bibliografia
[1] A. Troelsen Język C# 2010 i platforma .NET 4.0, PWN 2011
1
20

Podobne dokumenty