niedziela, 3 sierpnia 2008

Panowanie nad połączeniem bazy danych przez AOP

[Wymagania: postSharp, najlepiej sobie zainstalować przez "Windows Installer"]

AOP (Aspect Oriented Programming), czyli Programowanie Zorientowane Apektowo, więcej informacji co to jest i z czym to się je możecie znaleźć sobie w googlach ;), lecz całość opiera się na czymś takim:


// Pseudokod
main()
{
Wypisz("Cześć Wszystkim! :)");
}

Wypisz(string text)
{
Console.Writeln(text);
}

Wynik: Cześć Wszystkim! :)

Teraz chcemy by zaraz np. przed wywołaniem metody Wypisz() coś się wydarzyło czyli:

AspektPrzed
{
Console.Writeln("AOP mówi:");
}


main()
{
Wypisz();
}

[AspektPrzed]
Wypisz(string text)
{
Console.Writeln(text);
}

Wynik: AOP mówi: Cześć Wszystkim! :)

Tak czy siak zapraszam na:
http://www.postsharp.org/about/documentation/
http://www.postsharp.org/about/video/default.aspx <-- obowiązkowo! :)

Okey, teraz przejdźmy do naszego głównego problemu. Bardzo często nam się zdarza, że podczas korzystania z DAO, ręcznie tworzymy połączenia, zamykamy je, commitujemy transakcję, etc. Z odsieczą przychodzi właśnie AOP. Spójrzmy na przykład:


   1:  using (ConnectionManager cm = ConnectionManager.Create)

   2:  {

   3:      osoba.Save(cm);

   4:   

   5:      cm.CommitTransaction();

   6:  }


Wstawiliśmy rekord do tabeli, wszystko ok, lecz musieliśmy utworzyć połączenie i dać commita, a takiego kodu możemy mieć baaardzo dużo. Jeszcze możemy pokusić się o inne rozwiązanie projektu:

W klasie Osoba piszemy metodę Save(), lecz tak czy tak w niej musimy otworzyć to połączenie i zatwierdzić transakcję.

Zobaczmy przykład przy użyciu AOP, trochę będzie tego kodu ;):


   1:  osoba.Save(null);


Gotowe :)

A teraz krótkie wyjaśnienie jak to działa. Na samym początku tworzymy aspekt ConnectionAttribute : PostSharp.Laos.OnMethodInvocationAspect, OnMethodInvocationAspect znaczy, że będziemy "panować" nad metodami. Przeciążamy metodę OnInvocation i piszemy coś takiego:
public override void OnInvocation(PostSharp.Laos.MethodInvocationEventArgs eventArgs)
{
// Przechwytujemy wpisane parametry metody
object[] args = eventArgs.GetArgumentArray();

// Na pierwszym miejscu tablicy oczekujemy ConnectionManager
if (args[0] != null)
{
// Jesli istnieje, to wywolujemy metode z wczesniej wpisanymi parametrami
eventArgs.ReturnValue = eventArgs.Delegate.DynamicInvoke(args);

return;
}

using (ConnectionManager cm = ConnectionManager.Create)
{
// Jesli nie istniaje w args[0] ConnectioManager, to go wstawiamy
args[0] = cm;

// Wywolujemy metode (args[0] ma juz ConnectionManager'a)
eventArgs.ReturnValue = eventArgs.Delegate.DynamicInvoke(args);

// Commitujemy transakcje
cm.CommitTransaction();
}
}

Myślę, że tutaj dodatkowy komentarz jest zbędny ;)

Aby dana metoda "korzystała" z aspektu musimy dodać do niej atrybut:


   1:  [Connection] // Jest to nasz ConnectionAttribute, można podać jego całą nazwę jeśli chcemy

   2:  public void Save(ConnectionManager cm)



W zasadzie to by było na tyle, zapraszam do ściągnięcia pełnego przykładu, są tam umieszczone przeze mnie komentarze by można się było w tym połapać, ale na samym początku polecam użycie debugera i obserwowanie krok po kroku co się dzieje na ekranie.

Co do tego rozwiązania to nie wiem czy czasem nie da się go ulepszyć (zresztą znając życie na pewno się da ;)). Szukałem po sieci jakiegoś przykładu/wzorca AOP, który rozwiązuje ten problem, lecz nie znalazłem. Dlatego chętnie wysłucham krytykę (konstruktywną ;)) oraz to w jaki sposób można ulepszyć ten Aspekcik (i czy w ogóle to dobry kierunek). Jak macie jakieś uwagi zapraszam do wypowiedzi.

Brak komentarzy: