czwartek, 28 sierpnia 2008

Interview Questions

Może kogoś to zainteresuje:

ADO.NET part 1
ADO.NET part 2

ASP.NET

Jak dla mnie, autor odwalił kawał dobrej roboty.

wtorek, 12 sierpnia 2008

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.

piątek, 1 sierpnia 2008

I SystemPlacowy.DataAccessLayer (cz. 2)

[Ja zamiast tworzyć w Visual Studio Express wole pisać w SharpDevelop 3.0. Ma wbudowane narzędzia do testów, pokrycia kodu etc., lecz niestety brakuje mu paru rzeczy z VS, no ale to zawsze jakaś miła alternatywa ;)]

Dość gadania, bierzemy się za stworzenie warstwy dostępu do danych.
Projekty:



Z racji tego, że wszystko robimy w TDD (test driven development) zaczynamy od utworzenia testu połączenia z bazą.

[Test]
public void TestConnection()
{

}


Świetnie już prawie gotowe! To tyle ... żartowałem oczywiście ;)

Wiemy że dostęp będzie się odbywać przez klasę DataAccess. Skoro to wszystko musi być elastyczne, czyli DataAccess nie może być uzależniony od konkretnej bazy danych musimy stworzyć interface IDataProvider. Każdy dostawca danych będzie implementował ten interface:

public interface IDataProvider
{
IDbConnection Connection { get; }
}


Okey, rozszerzmy nasz test o dodatkową metodę:

[SetUp]
public void SetUp()
{
dataAccess = new DataAccess();
}


Atrybut [SetUp] uruchamia metodę JEDEN RAZ PRZED TESTEM.


Świetnie, lecz czegoś nam tu brakuje. Pasowałoby jakoś przekazać do DataAccess konkretnego providera. Może po prostu go wstrzykniemy przez konstruktor :)?

[SetUp]
public void SetUp()
{
dataAccess = new DataAccess(new MySqlDataProvider());
}


Kompilujemy projekt i proszę, niespodzianka! Nie chce się nam projekt kompilować. Brakuje tej klasy. Niezwłocznie stwórzmy ją w katalogu SystemPlacowy.DataAccessLayer.DataProvider:


public class MySqlDataProvider : IDataProvider
{
public MySqlDataProvider()
{
}

public IDbConnection Connection
{
get
{
return new MySqlConnection();
}
}
}


Teraz uruchamiamy test:


SharpDevelop 3.0

Zgodnie z oczekiwaniami test kończy się niepowodzeniem, ciekawe dlaczego ;) Lecimy do klasy MySqlDataProvider, po czym dopisujemy w getterze otwarcie połączenia.

public IDbConnection Connection
{
get
{
try
{
IDbConnection connection = new MySqlConnection(connectionString);
connection.Open();

return connection;
}
catch (MySqlException ex)
{
Console.WriteLine(ex.Message + " " + ex.StackTrace);

return new MySqlConnection();
}
}
}


Teraz test przechodzi :)



Podobnie robimy tak ze wszystkimi metodami jakie będziemy potrzebować (wszystko znajdziecie w kodzie źródłowym w 3 częsci).

Do tej pory wszystko mamy dobrze popisane, lecz dochodzimy do wniosku, że każde użycie DataAccess wiąże się z utworzeniem obiektu i wstrzyknięciem do niego MySqlDataProvider. Już to sobie wyobrażamy, że w każdymi miejscu w DAO tworzymy taki obiekt, bleeee. Na pomoc przychodzi nam (nieustraszony) Ninject, (podobnie jak Autofac jest kontenerem IoC).



Tworzymy klasę SystemPlacowy.DataAccessLayer.DataAccessModule : StandardModule. Przeciążamy metodę Load() i w jej ciele definiujemy moduł:

public class DataAccessModule : StandardModule
{
public override void Load()
{
Bind<DataAccess>().ToSelf();
Bind<IDataProvider>().To<MySqlDataProvider>();
}
}


W skrócie można to co się znajduje w Load() przetłumaczyć jako: wszędzie tam gdzie spotkasz IDataProvider to zastąp go MySqlDataProvider. Dzięki zastosowaniu takiego rozwiązania, w każdej chwili możemy sobie zmienić w tym JEDNYM I JEDYNYM miejscu w projekcie z MySqlDataProvider na inny np MsSqlDataProvider.

Zmieńmy nasz test:
[TestFixture]
public class TestDataAccess
{
private IKernel kernel;

[SetUp]
public void SetUp()
{
kernel = new StandardKernel(new DataAccessModule());
}

[Test]
public void TestConnection()
{
Assert.IsTrue(kernel.Get<DataAccess>().Connection.State == ConnectionState.Open);
}
}

Jest okey :) Yeah! Dobra robota... Nigdzie nawet nie użyliśmy "new" przy tworzeniu DataAccess, czy można chcieć od życia czegoś więcej? ;)

cdn...

NHibernate - materiały szkoleniowe

Bardzo ciekawa propozycja dla osób chcących się wdrożyć w NHibernate http://www.urlfan.com/site/summerofnhibernate_com/2800273.html. Nam się bardzo przyda w dalszych częściach tworzenia Systemu Płac i nie tylko.