piątek, 3 września 2010

Klasa dostępu do danych

To ostatni, przynajmniej na razie post o NHibernate. Jest on podsumowaniem wszystkich moich wysiłków w kierunku poznania podstaw tego ORMa. 
Wcześniejsze etapy prac przedstawiłem w postach o mapowaniach NHibernate'a oraz zarządzaniu sesjami.


Zmiany w klasie SessionManager
W porównaniu do klasy przedstawionej w poprzednim poście musiałem wprowadzić niewielkie zmiany. Zdecydowałem się jednak zrobić ją klasą statyczną oraz przeniosłem funkcję MakeTransaction do głównej klasy dostępu do danych, oto jej aktualny kod:
public static class SessionManager
{
 private static ISessionFactory sessionFactory;
 private static object syncObj = new object();
 private static ISessionFactory SessionFactory
 {
  get
  {
   if (sessionFactory == null)
    lock (syncObj)
    {
     if (sessionFactory == null)
     {
      sessionFactory = (new NHibernate.Cfg.Configuration())
       .Configure().BuildSessionFactory();
     }
    }

   return sessionFactory;
  }
 }

 public static ISession GetSession()
 {
  return SessionFactory.OpenSession();
 }
}
Zmiany zostały spowodowane małą reorganizacją podejścia do sposobu korzystania z klasy DataProvider. Początkowo miała ona mieć tylko jedną instancję dla całego programu i posiadać obiekt klasy SessionManager, jednak takie działania powodowały błędy sesji. Dlatego zdecydowałem się napisać coś podobnego do kodu, który widziałem w Summer of NHibernate.


Klasa DataProvider
Klasa zawiera jedną składową prywatną i jest nią obiekt implementujący interfejs ISession. Każdy obiekt dostępu do danych posiada własną sesję, na której operuje. Podczas tworzenia można przekazać obiekt sesji jako parametr lub w domyślnym konstruktorze uzyskać sesję z obiektu SessionManager (za pomocą statycznej funkcji GetSession()). Dodatkowo implementuje ona interfejs IDisposable, dzięki czemu można jej będzie używać w połączeniu z instrukcją using; jedyne co robi funkcja Dispose() to posprzątanie po sesji. 
private ISession session;
public DataProvider()
{
 session = SessionManager.GetSession();
}

public DataProvider(ISession _session)
{
 session = _session;
}

public void Dispose()
{
 if (session != null)
 {
  session.Dispose();
 }
}
Do tej klasy trafiła również z klasy SessionManager funkcja MakeTransaction, odpowiedzialna za wykonywanie w wygodny i bezpieczny sposób operacji modyfikujących stan bazy danych. Różni się od poprzedniej wersji jedynie tym, że sesja jest już składową klasy dlatego nie ma potrzeby jej tworzyć.
private void makeTransaction(Action<ISession> operation)
{
 using (var tx = session.BeginTransaction())
 {
  try
  {
   operation(session);

   tx.Commit();
  }
  catch (NHibernate.HibernateException)
  {
   tx.Rollback();
   throw;
  }
 }
}
Aby nie pisać wtórnych i powtarzających się metod podstawowych operacji (dodawania, usuwania, aktualizowania i pobierania obiektu po ID) dla każdej klasy osobno, napisałem cztery metody generyczne, pozwalające na wykorzystanie z dowolnym typem poprawnym dla mojej bazy.
T GetById(int id)
void Add(T obj)
void Delete(T obj)
void Update(T obj) 
Oprócz tego w miarę postępów projektu i pojawiania się nowych wymagań do obiektu dostępu do danych, będę pisał nowe funkcje pobierające specyficzne dane z bazy. Na razie w ramach zasady YAGNI nie zaprzątam sobie tym głowy.
Typowy sposób pracy z obiektem DataProvider może wyglądać następująco:
using (DataProvider dp = new DataProvider())
{
 Album album = dp.GetById<album>(1) as Album;
 album.Name = "New Name";

 dp.Update<album>(album);
}
Dzięki wykorzystaniu instrukcji using, po opuszczeniu bloku sesja obiektu jest uwalniana. Nowy obiekt DataProvider można tworzyć w każdym miejscu w programie, w którym jest potrzebny
Do tego napisałem zestaw kilku podstawowych testów sprawdzających każdą napisaną funkcję, wszystkie jak na razie przechodzą bez błędów. W tym momencie pracuję na testowej bazie, z nic nie znaczącymi danymi. Jednak, gdy nadejdzie odpowiedni moment będę musiał rozdzielić wyraźnie ją na bazę danych faktycznego programu i bazę danych do testów, ale w tym przypadku również traktuję to jako problem, którym nie muszę się na razie zajmować.


Krótkie podsumowanie prac z NHibernate
Opanowanie podstaw NHibernate'a zajęło mi znacznie więcej czasu niż początkowo na to planowałem. Nie do końca ze względu na to, że były one bardzo trudne. Przede wszystkim wpyw miał brak czasu na poświęcenie się nauce przez dłuższą chwilę i ciągłe "rozdrobnienie" na masę innych czynności. Poza tym Summer of NHibernate, pomimo iż świetnie zrobione i bardzo dobrze wytłumaczone krok po kroku, nie nadaje się do szybkiego opanowania materiału. Każdy odcinek ma sporo ponad godzinę, a czas na oglądanie nie był jedynym spędzonym przeze mnie nad kursem. Następną nową wiedzę będę zdobywał z tutoriali lub wprost z dokumentacji, mam nadzieję, że będzie szybciej.
Na razie poznałem znikomy ułamek możliwości NHibernate'a, ale to co zobaczyłem przekonuje mnie do tego, że warto z niego korzystać (lub z innego ORMa) oraz że prawdziwe jest zdanie "Pisząc ręcznie dostęp do danych OKRADAMY pracodawcę/klienta". Niezbędne podstawy opanowałem, a jeśli kiedyś przyjdzie potrzeba wykorzystania zaawansowanych funkcjonalności będę gotów jej sprostać.

Brak komentarzy:

Prześlij komentarz