W minioną sobotę miałem przyjemność uczestniczyć w krakowskiej edycji Visual Studio Community Launch. Co prawda, jako jeden z organizatorów, nie jestem najlepszą osobą do obiektywnej oceny tego wydarzenia, ale moim zdaniem było super.

Podczas VSCL miałem okazję sprawdzić się także w roli prelegenta, prowadząc dwie piętnastominutowe mikroprezentacje dotyczące Entity Framework 4 oraz Windows Communication Foundation 4. Przykłady kodu dla obu prezentacji umieściłem na MSDN Code Gallery odpowiednio tutaj i tutaj. Zapewne duża część z Was nie była na konferencji, dlatego postanowiłem owe przykłady omówić tutaj, na blogu. Dziś – EF4.

W obu przypadkach starałem się wpleść prezentację nowych funkcji danej technologii w jakąś większą historię. Dla Entity Framework tą większą historią jest możliwość współdzielenia (części) modelu z aplikacją napisaną w NHibernate.

Design

Solution wygląda tak:

Znajdują się w nim dwie aplikacje: obsługa zamówień (Orders) oraz bank (Accounts). Obie aplikacje składają się z prostego programu demonstrującego działanie, modelu domeny (Model) oraz obsługi persystencji (DataAccess). Współdzielony fragment to Parties – projekt niezależny od technologii persystencji (bez referencji do NHibernate lub EF) zawierający implementację podmiotów (osób fizycznych i organizacji).

POCO

Jednym z celów nowej wersji Entity Framework było wsparcie dla budowy modeli POCO (Plain Old CLR Object). Dlaczego jest to takie ważne? Ponieważ właśnie obsługa POCO pozwala wykorzystywać w EF klasy, które nie były napisane konkretnie z myślą o nim oraz, patrząc z drugiej strony, pozwala wykorzystać klasy napisane specjalnie dla EF w innych kontekstach. POCO daje nam komfort braku zależności od frameworku ORM na poziomie modelu domeny – coś, czego nie sposób przecenić. Dla przykładu, klasa Party reprezentująca podmiot wygląda tak:

public class Party
{
   public virtual int Id { get; protected set; }
   public virtual Address Address { get; protected set; }

   protected Party(Address address)
   {
      Address = address;
   }

   protected Party()
   {
   }
}

Lazy Loading

Leniwe ładowanie (lazy loading) jest kolejnym wielkim nieobecnym z pierwszej wersji EF, który doczekał się implementacji w wersji drugiej (czwartej). Jest ono jednak domyślnie wyłączone. Aby je włączyć, należy dodać do konstruktora kontekstu następującą linię:

ContextOptions.LazyLoadingEnabled = true;

Wszystko jednak ma swoją cenę. Ceną, którą płacimy za obsługę leniwego ładowania w EF jest konieczność „wirtualizacji” wszystkich property klas modelu. Jest to niezbędne, ponieważ EF w trakcie działania systemu generuje dynamicznie klasę dziedziczącą po naszej. Nie jest to jednak wielki problem, ponieważ NHibernate ma analogiczne wymaganie. Możemy więc spokojnie przywyknąć do myśli, że standardem dla ORM jest konieczność deklarowania wirtualnych property.

Value Objects

Value Objects są nazywane w EF „Complex Type”. Ot, taka fanaberia ludzi z Microsoft. Complex Type w EF może być modyfikowalny. Ja osobiście jednak odradzałbym to ze wszystkich. Niemodyfikowalność Value Object jest bardzo ważną cechą, m.in. ułatwiającą testowanie.

Ponieważ dla Complex Type EF nie prowadzi change tracking’u (sprawdzenie, czy należy zaktualizować bazę wykonywane jest za pomocą porównania wartości aktualnych i pobranych), nie ma sensu wirtualizacja properties CT. Znowu, jest to zgodne z tym, jak ja pracuje z NHibernate. Moje Value Objects muszą być jak najbardziej niezależne kontekstu bazodanowego. Jedyne na co się mogę (i niestety muszę) zgodzić to pusty konstruktor z widocznością protected. Zarówno EF, jaki NHibernate go wymagają. Efektem tego zestawu wymagań jest klasa Address będąca Value Objectem reprezentującym adres podmiotu:

public class Address
{
   public string Street { get; private set; }
   public string BuildingNumber { get; private set; }
   public string City { get; private set; }
   public string Country { get; private set; }

   protected Address()
   {
   }

   public Address(string street, string buildingNumber, string city, string country)
   {
      Street = street;
      BuildingNumber = buildingNumber;
      City = city;
      Country = country;
   }
}

Enkapsulacja

Enkapsulacja to zawsze dobra rzecz. Nie inaczej jest w wypadku modelu domeny. Upublicznianie getterów (a broń Boże setterów) właściwości jest prostą drogą do degradacji naszego modelu do roli prostych struktur danych. A przecież nie o to nam wszystkim chodzi. Z tego powodu bardzo się zmartwiłem po przeczytaniu na MSDN opisu wymagań dla POCO w EF. Wynika z niego (chyba, że ja źle rozumiem), że aby leniwe ładowanie działało, wszystkie właściwości klasy muszą być „public virtual”.

Na szczęście przeglądając sieć natknąłem się na wzmiankę, iż wystarczy „protected virtual”. Postanowiłem sprawdzić. Jakaż była moja radość, gdy okazało się, że wszystko działa. W kontekście enkapsulacji EF4 ma analogiczne wymagania, co NHibernate. Yupi:-)

Klasa Account jest świetnym przykładem enkapsulacji dla kolekcji: lista operacji związanych z kontem nie może być modyfikowana bezpośrednio, ale jedynie poprzez wywołania Credit lub Debit:

public class Account
{
   //...
   public decimal Balance { get; protected set; }
   protected virtual IList<Operation> Operations { get; set; }

   //...
   public virtual void Debit(decimal amount, string title)
   {
      if (Balance - amount < 0)
      {
         throw new InvalidOperationException();
      }
      var op = new Operation(-amount, title);
      Operations.Add(op);
      Balance -= amount;
   }

   public virtual void Credit(decimal amount, string title)
   {
      var op = new Operation(amount, title);
      Operations.Add(op);
      Balance += amount;
   }
}

Podsumowanie

Druga edycja Entity Framework jest o niebo lepsza od poprzedniej. Właściwie mogę powiedzieć z czystym sumieniem, że jest nawet używalna w kontekście Domain-Driven Design. Cieszy mnie to niezmiernie, bo lubię mieć wybór. Pluralizm to dobra rzecz. Czy rozważam przesiadkę z NHibernate na EF? Na pewno nie w tej wersji. Przewaga NH w przypadku bardziej skomplikowanych kwestii jest jeszcze zbyt duża. Z drugiej strony nie potrzebuję (i nie będę potrzebował) wszystkich tych kwestii związanych z obsługą architektur n-tier, w które Microsoft pakuje tyle pary.

Jeśli jednak EF będzie się rozwijać w tak szybkim tempie, jest wysoce prawdopodobne, że wersja 6.0 będzie stanowić groźną konkurencje dla NHibernate także w kontekście DDD.

VN:F [1.9.13_1145]
Rating: 3.0/5 (1 vote cast)