niedziela, 14 listopada 2010

Podsumowanie dotychczasowych poczynań w projekcie.

Witam, na ostatniej prostej w konkursie byłem dosyć mało aktywny, związane to było z praca i studiami.

Wracając do podsumowania. W początkowej fazie projektu(czyli podczas trwania konkursu :)) skupiłem się głównie na stworzeniu mocno funkcjonalnej podstawy na której można budować aplikacje www. W moim wypadku jest to forum. W ramach tej podstawy wchodzą interfejsy do obsługi bazy danych z pomocą Nhibernate, wiele przydatnych wzorców i narzędzi(Event Aggregator, wzorzec specyfikacji, mechanizmy wspomagające walidacje jak również narzędzia związane bliżej z samym budowaniem stron www tak jak AssetManager czy ViewModele). W zasadzie ten pierwszy etap został zakończony. Oczywiście nie można przewidzieć wszystkiego co będzie potrzebne baza już jakaś jest.

Niestety ostatnio nie mogłem poświecić wiele czasu na projekt. W związku z tym nie jest on funkcjonalny od strony prezentacyjnej. Cóż niestety tak wyszło :) W każdym razie to tyle i życzę powodzenia sobie jak i innym uczestnikom konkursu.

sobota, 2 października 2010

Cache - rozmyślania i implementacja

Aktualnie pracowałem nad projektem UI do mojego forum. W zasadzie takie w miarę proste rozwiązanie już jest gotowe. Wiec postanowiłem zabrać się za implementacje powoli podstawowych widoków. I tutaj pojawił się pierwszy problem. A mianowicie zarządzanie, kompresja i cachowanie plików CSS i JS. Jak że zwykle mamy w projekcie jakieś stałe skrypty(np. jQuery) które są wykorzystywanie przy każdym widoku wypadało by żeby one były cachowane i skompresowane. Wiec zabrałem się za rozwiązanie tego problemu czyli implementacje wrappera do standardowego mechanizmu cache który jest dostępny w ASP.NET. Tak samo jak z HttpContextem chciałbym aby był on silnie typowany.

Interfejs i klasa Cache
Jak się pewnie domyślacie klasa cache bedzie implementowała interfejs ICache. Najpierw przedstawie jak ona wygląda poźniej krótki opis.
public interface ICache
{
    void Set<T>(string key, object value, DateTime absoluteExpiration,
 TimeSpan slidingExpiration);
    void Set<T>(string key, object value, DateTime absoluteExpiration);
    void Set<T>(string key, object value, TimeSpan slidingExpiration);
    T Remove<T>(string key);
    void Clear();
    T Get<T>(string key);
}
Interfejs posiada kilka metod które ustawiają wartość w cache. Różnią się one zmiennymi dotyczącymi wygasania danej wartości z cache. Zmienna AbsoluteExpiration określa bezwzględna datę wygaśnięcia danego obiektu czyli natomiast SlidingExpiration nie określa daty wygaśnięcia a jakiś odcinek czasu po którym ma wygasnąć dany wartość. Reszta nie powinna budzić wątpliwości. Sama implementacja wygląda tak:
public class Cache : ICache
{
 //pomijam deklaracje zmiennych i funkcje set
 ...
 
    public T Remove<T>(string key)
    {
        return (T) _cache.Remove(key);
    }

    public void Clear()
    {
        IDictionaryEnumerator cacheEnumerator = _cache.GetEnumerator();

        while (cacheEnumerator.MoveNext())
        {
            _cache.Remove(cacheEnumerator.Key.ToString());
        }
    }

    public T Get<T>(string key)
    {
        Check.Argument.IsNotEmpty(key, "key");

        lock (_syncObject)
        {
            T cacheItem = (T)_cache.Get(key);

            if (cacheItem == null)
                return default(T);
            else
                return cacheItem;
        }
    }

    private void DoSet<T>(string key, object value, 
DateTime absoluteExpiration, TimeSpan slidingExpiration)
    {
        Check.Argument.IsNotEmpty(key, "key");
        Check.Argument.IsNotNull(value, "value");

        if (_cache[key] == null)
            _cache.Add(key, value, null, absoluteExpiration, slidingExpiration,CacheItemPriority.Normal, null);
        else
            _cache.Insert(key, value, null, absoluteExpiration, 
slidingExpiration);
    }
}
Wstawiłem tylko najważniejsze części kodu, pominąłem wszystkie wersje fukcji set ponieważ odwołują się one do prywatnej funkcji DoSet z różnymi parametrami. Jak widać metody tutaj operują na cache z HttpRuntime.Cache. Funkcja Clear pobiera enumerator z kolekcji i usuwa wszystkie obiekty z cache. Funkcja Get pobiera wartość z cache, jeżeli nie istnieje to zwraca wartość domyślną dla danego obiektu. Jest ona thread-safe(bezpieczna wielowątkowo). Natomiast metoda DoSet wstawia obiekt do cache. Sprawdza ona czy już istnieje obiekt o danym kluczu. Jeżeli tak to zamiast dodawać tylko wstawia nową wartość do niego.

W następnym poście powinienem już pokazać mechanizm związany z cachowaniem plików CSS i JS. Oczywiście dodałem testy związane z cachowaniem. Oprócz tego powoli wrzuciłem interfejs graficzny jaki będzie obowiązywał na forum.

sobota, 25 września 2010

HttpContext - Problemy i implementacja wrappera.

Witam dziś podczas kodowania natrafiłem na dosyć nieprzyjemne utrudnienie. Mianowicie dotyczy ono wstrzykiwania poprzez IoC HttpContext i pobierania aktualnego kontekstu do klas które tego potrzebują np. sesja. ciasteczka itp. Jak wiadomo HttpContext jest trudno dostępnym obiektem i ciężko w łatwy sposób wstrzyknąć. Wiec postanowiłem napisac wrapper do HttpContext.

HttpContextProvider
Nasz obiekt będzie dziedziczył po klasie IHttpContextProvider. Będzie on posiadał podstawowe zmienna które pozwolą nam na dostanie się do wszystkich parametrów aktualnego HttpContextu. Obiekt bedzie wyglądał tak:
public class HttpContextProvider : IHttpContextProvider
{

    public System.Web.HttpContextBase Context
    {
        get { return new HttpContextWrapper(HttpContext.Current); }
    }

    public System.Web.HttpRequestBase Request
    {
        get { return Context.Request; }
    }

    public System.Web.HttpResponseBase Response
    {
        get { return Context.Response; }
    }

    public System.Collections.Specialized.NameValueCollection FormOrQueryString
    {
        get
        {
            if (Request.RequestType == "POST")
            {
                return Request.Form;
            }
            return Request.QueryString;
        }
    }
}
Generalnie nic specjalnie trudnego dziwnego w tym obiekcie. Jedyny co może wzbudzić ciekawość to tajemniczy klasa HttpContextWrapper z biblioteki System.Web. Ta klasa dziedziczy po HttpContextBase i pozwala na dostanie się do funkcjonalności aktualnego kontekstu. Dzięki takiemu rozwiązaniu mamy łatwy dostęp do HttpContextu z kontenera IoC jak i również ułatwia nam to testy ponieważ jak większość z was wie stworzenie fałszywego kontekstu jest dosyć nieprzyjemnym zadaniem.

Session Manager
Wszystkie powyższe wypociny były związane z tym ze chciałem stworzyć obiekt do zarządzania sesją użytkownika. Główne rozwiązanie które chciałem dodać silnie typowana obsługa sesji. Klasa sessionManagera wygląda tak:
public class SessionManager : ISessionManager
{
    private IHttpContextProvider _httpContextProvider;

    public SessionManager(IHttpContextProvider provider)
    {
        _httpContextProvider = provider;
    }

    public void Set<T>(string name, T value)
    {
        _httpContextProvider.Context.Session.Add(name, value);
    }

    public T Get<T>(string name)
    {
        return (T)_httpContextProvider.Context.Session[name];
    }

    public T TryGet<T>(string name)
    {
        try
        {
            return (T)_httpContextProvider.Context.Session[name];
        }
        catch (NullReferenceException e)
        {
            return default(T);
        }
    }

    public void Abandon()
    {
        _httpContextProvider.Context.Session.Abandon();
    }
}
Klasa posiada metody podstawowe do pobierania i ustawiania wartości w sesji jak i również porzucania sesji. Oprócz tego dodałem metodę TryGet która pobiera wartość z sesji natomiast gdy nie istnieje to zwraca standardową wartość dla danego typu. Podczas zwykłego pobierania metoda Get wyrzuci wyjątek NullReferenceException co czasami jest niepożądane. Jest to coś podobnego jak mamy w słowniku. Natomiast niestety kolekcja sesji nie jest słownikiem. To tyle na dziś.

niedziela, 19 września 2010

Podstawowe konfiguracje - pliki konfiguracyjne w Asp.net Mvc

Dziś chciałbym pokazać rozwiązanie problemu związanego ze sposobem przechowywania podstawowych informacji o forum np, nazwa, słowa kluczowe.

Podczas gdy zastanawiałem się nad rozwiązaniem problemu najprostszym pomysłem jest po prostu przechowywanie tego w bazie danych. Po głębszych przemyśleniach stwierdziłem ze dla podstawowych informacji nie ma sensu dodatkowo obwiązać bazy danych wiec postanowiłem skorzystać z plików konfiguracyjnych które są dostępne na platformie nie. Jako że i tak one są wykorzystywane podczas tworzenia serwisu wiec nie powoduje to żadnego zwieszonego obciążenia związanego z odczytywaniem tych danych.

Pliki konfiguracyjne
Jak większość wie pliki konfiguracyjne są podzielone na sekcje. Na początek dla naszego forum stworzymy podstawową sekcje która będzie przechowywała najprostsze informacje. Wygląda ona tak:
<mForum SiteTitle="mForum"
        MetaKeywords="Forum,Board,Project,.Net"
        MetaDescription="mForum is going to be fully featured forum implemented in C# on .NET platform"
        MinPasswordLength="5">
</mForum>
Jednak żeby za bardzo nie mieszać postanowiłem konfiguracje dotyczacą forum przenieść do oddzielnego pliku konfiguracyjnego. Aby nasze forum o tym wiedziało w głównym web.configu musimy wstawic taka linie:
<mForum configSource="mForum.config"/>

Klasa Settings
Klasa ta będzie przechowywała informacje dotyczące wszystkich ustawień forum. Nic w tej klasie specjalnego. Zmienne i obiekty które będą zawierały dane z pliku konfiguracyjnego.
public class Settings
{
    public Settings(string siteTitle, string metaKeywords, string metaDescription, int minPasswordLength)
    {
        Check.Argument.IsNotEmpty(siteTitle, "siteTitle");
        Check.Argument.IsNotEmpty(metaKeywords, "metaKeywords");
        Check.Argument.IsNotEmpty(metaDescription, "metaDescription");
        Check.Argument.IsNotNull(minPasswordLength, "minPasswordLength");
        Check.Argument.IsNotNegativeOrZero(minPasswordLength, "minPasswordLength");

        SiteTitle = siteTitle;
        MetaKeywords = metaKeywords;
        MetaDescription = metaDescription;
    }

    public string SiteTitle 
    { 
        get; 
        internal set; 
    }

    ...

}
Klasa SettingsConfigurationSection
Tutaj mamy natomiast obiekt który będzie wykorzystywany w bezpośrednim wyciąganiu danych z plików konfiguracyjnych. Dane z sekcji są bindowane do tego obiektu. Sam obiekt dziedziczy po klasie ConfigurationSection. Wygląda tak:
public class SettingsConfigurationSection : ConfigurationSection
{
    private static string _sectionName = "mForum";

    public static string SectionName
    {
        [DebuggerStepThrough]
        get
        {
            return _sectionName;
        }

        [DebuggerStepThrough]
        set
        {
            Check.Argument.IsNotEmpty(value, "value");

            _sectionName = value;
        }
    }


    [ConfigurationProperty("SiteTitle", DefaultValue = "mForum")]
    public string SiteTitle
    {
        [DebuggerStepThrough]
        get
        {
            return (string)this["SiteTitle"];
        }

        [DebuggerStepThrough]
        set
        {
            this["SiteTitle"] = value;
        }
    }

    [ConfigurationProperty("MetaKeywords", DefaultValue = "")]
    public string MetaKeywords
    {
        [DebuggerStepThrough]
        get
        {
            return (string)this["MetaKeywords"];
        }

        [DebuggerStepThrough]
        set
        {
            this["MetaKeywords"] = value;
        }
    }
    
    ...
}
Składa się on z zmiennych statycznych które bezpośrednio pobierają dane z sekcji. Oprócz tego jest jedna zmienna która jest na stałe ustawiania. Jest to nazwa sekcji.
Inicjowanie klasy Settings odbywa się podczas rejestracji kontenera IoC jak w przypadku wszystkich innych obiektów. Nie jest to nic skomplikowanego. Zapraszam do źródeł po dokładna implementacje.

poniedziałek, 13 września 2010

Komunikacja miedzy usługami a kontrolerem...

Witam dziś chciałbym poruszyć kolejny problem na który się natknąłem podczas rozwijania projektu. Problem dotyczy przesyłania danych pomiędzy kontrolerem a usługami(service). Weźmy prosty przykład. Mamy funkcje która z pobranych danych od użytkownika tworzy temat i post na forum następnie zwraca id zarówno tematu jak i posta. I tutaj pojawia się problem gdy potrzebujemy zwrócić wiele danych z danej funkcji z usług.

Podczas moim przemyśleń postanowiłem wzorować się na mechanizmie żądań i odpowiedzi jaki zwykle stosuje się w WCF usługach. My oczywiście ich nie używamy ale dzięki temu nasz projekt będzie przygotowany na taka ewentualność.

Komunikacja miedzy usługa a kontrolerem
Komunikacja między usługą a kontrolerem wygląda dosyć prosto. Jak może się z początku wydawać mechanizm który stosuje niepotrzebnie tylko zaciemnia kod ale w miarę rozwoju projektu ustandaryzowana komunikacja będzie pomagać otrzymywać i rozwijać projekt. Wyróżniam 2 typy wiadomości: Żądanie(request) i Odpowiedz(response). Odpowiadają im odpowiednia typy: BaseRequestMessage i BaseResponseMessage. Oba typy dziedziczą po BaseMessage. Przykładowy żądanie i odpowiedź:
public class LogOnRequest : BasicRequestMessage
{
    public LogOnDTO LogOnDTO { get; set; }
}
A tutaj mamy odpowiedź:
public class LogOnResponse : BasicResponseMessage
{
    public bool isValidated { get; set; }
}

Dzięki takiemu mechanizmowi komunikacja wygląda czytelnie i ładnie jak i mamy możliwość odbieranie wielu danych z usługi. Oczywiście odpowiednia zmodyfikowałem usługi i testy aby współpracowały z naszym nowym mechanizmem. Ale tego już nie będę opisywał tylko zapraszam do przeglądania źródeł.

czwartek, 9 września 2010

Globalne filtry i wstrzykiwanie zależności...

Witam dziś chciałbym nieco przybliżyć globalne filtry z MvcExtensions i jedno z zastosowań które będzie u mnie w systemie działo za pomocą tego mechanizmu.

Prosty problem: Chcemy łatwo mieć możliwość na bieżąco uaktualniać informacje o ostatnich czasie kiedy user przeglądał stronę bądź zapisywać wykonane przez niego akcje. Gdy pierwszy raz podchodziłem do tego problemu przychodziły mi 2 rozwiązania:
-Pierwsze: Stworzyć BaseController z którego będą dziedziczyły wszystkie inne a który będzie posiadał metodę która będzie wykonywana za każdym razem gdy wykonamy akcje kontrolera.
-Drugie: Użycie filtrów nakładanych na dany kontroler.

Pierwsze rozwiązanie wydaje się dosyć fajnie jednak kłóci się to z zasada ze kontrolery nie powinny posiadać w sobie logiki. Poza tym staja się trudno testowalne poprzez taki zabieg. Wiec postanowiłem skorzystac z drugiego sposobu. Jak się później okazało problemem związanym z atrybutami jest wstrzykiwanie potrzebnych zależności do wykonania konkretnej czynności. Poszperałem troszkę i znalazłem w MvcExtension mechanizm zwany globalnymi filtrami. Notabene jest on zaimplementowany w wersji 3 Mvc frameworka.

UpdateUserLastActivityFilter
Jak już wcześniej mówiłem pierwszym prostym filtrem który będę wykorzystywał jest uaktualnianie ostatniej aktywności użytkownika. Sam atrybut jest prosty. Jeżeli użytkownik jest zalogowany to wywołuje metodę z klasy UserService która uaktualnia jego aktywność. Atrybut wygląda tak:
[AttributeUsage(AttributeTargets.Method | AttributeTargets.Class, Inherited = true, AllowMultiple = false), CLSCompliant(false)]
public class UpdateUserLastActivityAttribute : FilterAttribute, IResultFilter
{
    public UpdateUserLastActivityAttribute(IUserService userService)
    {
        Check.Argument.IsNotNull(userService, "userService");

        UserService = userService;
    }

    public IUserService UserService
    {
        get;
        private set;
    }

    public void OnResultExecuting(ResultExecutingContext filterContext)
    {

    }

    public void OnResultExecuted(ResultExecutedContext filterContext)
    {
        Check.Argument.IsNotNull(filterContext, "filterContext");

        string username = filterContext.HttpContext.User.Identity.IsAuthenticated ? filterContext.HttpContext.User.Identity.Name : null;

        if (!string.IsNullOrEmpty(username))
        {
            UserService.UpdateLastActivity(username);
        }
    }
}
Posiadamy już atrybut teraz musimy znaleźć sposób na wstrzykniecie UserService do tego atrybutu. W bibliotece MvcExtension jest zdefiniowany specjalny interfejs który pomaga nam w tym zadaniu. Nazywa sie ConfigureFilterBase. Implementujemy go jako Bootstrapper task. Wygląda to tak:
public class RegisterGlobalFilters : ConfigureFiltersBase
{
    protected override void Configure(IFilterRegistry registry)
    {
        registry.Register<AccountController, UpdateUserLastActivityAttribute>()
                .Register<HomeController, UpdateUserLastActivityAttribute>();
    }
}
Jak widać rejestruje się filtry podobnie jak typy w IoC kontenerze. Wygląda to tak że jako pierwszy typ podajemy dany kontroler natomiast kolejne następne są to filtry które są do niego podczepiane. Takim zabiegiem umożliwiamy wstrzykniecie niezbędnych interfejsów do atrybutów. Poza tym nie musimy już ręcznie dekorować kontrolera ta akcja.

To tyle jeżeli chodzi o globalne filtry. Jak widać MvcExtension posiada wiele ciekawych rozwiązań. Wiele z nich bedzie dostepna w przyszlych wersjach Asp.net Mvc.

niedziela, 5 września 2010

Walidacja obiektów domeny i nie tylko...

Witam troszkę teraz nie regularnie pisałem z powodu braku czasu ale już rozpoczął się wrzesień wiec czas do roboty. Dziś chciałbym omówić problem walidacji obiektów domenowych ale i nie tylko. Interfejs ten będzie można wykorzystać przy dowolnym obiekcie. Cele które chciałbym osiągnąć są 2: Przede wszystkim walidacja musi być wielokrotnego użytku i łatwo dostępna. Drugi cel to możliwość walidacji odrazu całego obiektu i zwrócenie do kontrolera informacji o wszystkich błędnych polach.

Interfejs IValidatable
Zacznijmy od samego początku ponieważ walidacja musi być łatwo dostępna i wielokrotnego użytku każdy obiekt który będzie posiadał walidacje musi implementować powyższy interfejs. Nic specjalnego:
public interface IValidatable
{
    ValidationResult Validate();
}

ValidationResult
Klasa ta składa się ze słownika który przechowuje informacje o błędzie jak i pole którego dotyczy informacja. Natomiast z powodu ze dane pole może posiadać wiele błędów dlatego przechowuje listę stringów. Oprócz tego posiada 2 funkcje 1 do zliczania błędów a druga do dodawania błędów do słownika. Sama klasa wygląda tak:
public class ValidationResult
{
    private IDictionary<string, IList<string>> _validationErrors;

    public ValidationResult()
    {
        _validationErrors = new Dictionary<string, IList<string>>();
    }

    public IDictionary<string, IList<string>> Errors
    {
        get { return _validationErrors; }
        private set { _validationErrors = value; }
    }

    public int CountErrors()
    {
        int errorCount = 0;

        foreach (var errorMessages in _validationErrors.Values)
        {
            errorCount += errorMessages.Count;
        }

        return errorCount;
    }

    public void AddError(KeyValuePair<string, string> error)
    {
        if (error.Key != null)
        {
            if (_validationErrors.ContainsKey(error.Key))
            {
                _validationErrors[error.Key].Add(error.Value);
            }
            else
            {
                IList<string> newErrorList = new List<string>();
                newErrorList.Add(error.Value);

                _validationErrors.Add(new KeyValuePair<string, IList<string>>(error.Key, newErrorList));
            }
        }
    }
}

ValidationHelper
Żeby troszeczkę sobie ułatwić dodawanie błędów postanowiłem stworzyć klasę pomocnicza która będzie nieco automatyzować dodawanie błędów. Będzie to obiekt który posiada 2 metody które dodają błąd jeżeli warunek jest niespełniony. Różnią się tylko 1 parametrem związanym z kluczem błędu. Zwykle nazywa się tak samo jak dane pole którego dotyczy. I w tym celu służy wersja metody z wyrażeniem lambda. Natomiast druga przyjmuje parametr string. Czyli jest bardziej uogólniona. Klasa wygląda tak:
public class ValidationHelper<T>
{
    public ValidationHelper()
    {

    }

    public KeyValuePair<string, string> CreateErrorIf(bool condition, string errorKey, string errorMessageKey)
    {
        if (!condition)
            return new KeyValuePair<string, string>(errorKey, ResourceHelper.GetErrorMessage(errorMessageKey));
        else
            return new KeyValuePair<string, string>();
    }

    public KeyValuePair<string, string> CreateErrorIf<TResult>(bool condition, Expression<Func<T, TResult>> errorKey, string errorMessageKey)
    {
        var expressionMember = errorKey.Body as MemberExpression;

        return CreateErrorIf(condition, expressionMember.Member.Name, errorMessageKey);
    }
}
Metoda która używa wyrażenia lambda po prostu pobiera z niego nazwę danego pola. A teraz przykładowe wykorzystanie powyższych klas:
public class User : BaseEntity, IEntity, IValidatable
{        
    ...

    public virtual ValidationResult Validate()
    {
        ValidationResult validationResult = new ValidationResult();
        ValidationHelper<User> validationHelper = new ValidationHelper<User>();

        validationResult.AddError(validationHelper.CreateErrorIf(!String.IsNullOrEmpty(Username), f => f.Username, ResourceKey.Common.RequiredField));
        validationResult.AddError(validationHelper.CreateErrorIf(Username.Length >= 256, f => f.Username, ResourceKey.Common.FieldLength));

        ...

        return validationResult;
    }
}

Oprócz pisania tego mechanizmu powolutku uzupełniam brakujące testy w projekcie związane już z gotową funkcjonalnością. To tyle na dziś :)

wtorek, 24 sierpnia 2010

Nhibernate - relacje jeden do wielu i pare zmian kosmetycznych

Witam po dosyć długiej przerwie spowodowanej wakacjami :) Dziś postanowiłem troszeczkę opisać bardzo uproszczony model logiki związanej ścisłe z forum czyli encje takie jak Kategoria forum, Forum, Temat, Post. Oprócz tego opisze parę kosmetycznych.

Logika związana z forum
Forum będzie składało się z kategorii. Dzielą one całe forum na części związane ze sobą tematycznie. Każda kategoria możne posiadać wiele forów. W skład forów wchodzą tematy które zaś maja wiele postów. Aktualnie modele są bardzo proste. Wraz z rozwojem projektu i zwiększaniem funkcjonalności będą się powiększać
Klasa Kategorii forum:
public class ForumCategory : BaseEntity
{
    public virtual string Name { get; set; }

    public virtual IEnumerable<Forum> Forums { get; set; }
}
Klasa Forum:
public class Forum : BaseEntity
{
    public virtual string Name { get; set; }

    public virtual string Description { get; set; }

    public virtual ForumCategory Category { get; set; }

    public virtual IEnumerable<Topic> Topics { get; set; }
}
Klasa Temat:
public class Topic : BaseEntity
{
    public virtual string Name { get; set; }

    public virtual Forum Forum { get; set; }

    public virtual IEnumerable<Post> Posts { get; set; }
}
Klasa Post:
public class Post : BaseEntity
{
    public virtual string Name { get; set; }

    public virtual Topic Topic { get; set; }
}
Nhibernate - relacje jeden do wielu
Jak widać powyżej w naszym prostym modelu encji parę relacji: jeden do wielu. W Fluent Nhibernate relacje te mapuje się przez metody References i HasMany. References oznacza to samo co metoda HasOne czyli że encja posiada 1 obiekt w danej relacji. Natomiast HasMany oznacza ze dana encja posiada wiele obiektów z danej relacji. Kombinacja tych 2 metod powoduje powstanie relacji 1 do wielu. Teraz prosty przykład mapowanie relacji jeden do wielu: ForumCategory -> Forum
public class ForumCategoryMap : ClassMap<ForumCategory>
{
    public ForumCategoryMap()
    {
        Id(p => p.Id).GeneratedBy.Guid();

        Map(p => p.Name).Not.Nullable().Length(256);

        HasMany(p => p.Forums);
    }
}
A tutaj mamy druga końcówkę naszej relacji czyli klasę Forum:
public class ForumMap : ClassMap<Forum>
{
    public ForumMap()
    {
        Id(p => p.Id).GeneratedBy.Guid();

        Map(p => p.Name).Not.Nullable().Length(256);
        Map(p => p.Description).Not.Nullable().Length(256);

        References(p => p.Category);
        HasMany(p => p.Topics);
    }
}
Jak widać w bardzo prostu sposób za pomocą Fluent Nhibernate jesteśmy w stanie stworzyć relacje jeden do wielu.

Oprócz tych zmian wprowadziłem podstawowy layout forum. Usunąłem commandy. Ich role przejma teraz DataTransferObjecty. Podmieniłem walidacje po stronie klienta z standardowej Microsoftu na jquery. Wymagało to ściagniecia MVC Futures i podmiany w widokach pliku MicrosoftMvcJQueryValidation.js zamiast MicrosoftMvcValidation.js.

sobota, 14 sierpnia 2010

Zasoby(Resources) - czyli wielojezyczne strony

Dziś krotki wpis na temat wielojęzycznych stron. Będę opierał się na plikach .resx. Mechanizm jest wbudowany w sama platformę net ale opisze co i jak rozwiązałem w praktyce.

ResourceHelper
Jest to nasz obiekt pomocniczy ułatwiający pobieranie wartości z plików .resx. Jest rownież odpowiedzialny za inicjalizacje ResourceManagera czyli głównego obiektu od strony platformy .NET który pozwala zarządzać zasobami. Sama klasa nie jest skomplikowana. Oprócz metody inicjalizujacej posiada tylko jedna metodę która pobiera wartość typu string z naszych plików z zasobami. Klasa wygląda ona tak:
public static class ResourceHelper
{
    private static ResourceManager _resourceManager;

    public static void Initialize(ResourceManager resourceManager)
    {
        Check.Argument.IsNotNull(resourceManager, "resourceManager");

        _resourceManager = resourceManager;
    }

    public static string GetErrorMessage(string key)
    {
        string errorMessage = _resourceManager.GetString(key);

        if (errorMessage == null)
            throw new ArgumentException(_resourceManager.GetString(ResourceKey.Common.InvalidMessage));

        return errorMessage;
    }
Sama klasa jest statyczna. Jak można zauważyć występuje tutaj również klasa o nazwie ResourceKey. Ta klasa posiada wszystkie informacje na temat nazw danych zmiennych w plikach .resx. Po prostu nie musimy zawsze wpisywać danego stringa tylko używamy klasy pomocniczej i w wypadku jakiś zmian tylko w jednym miejscu będzie to potrzebne. Takie małe ułatwienie :) W nowo dodanym projekcje oprócz plików .resx jest tez fabryka która generuje nam odpowiedniego resourceManagera którym to później inicjujemy naszego helpera. Wygląda ona bardzo prosto:
public class ResourceManagerFactory : IResourceManagerFactory
{
    private ResourceManager _resourceManager;

    public ResourceManagerFactory()
    {

    }

    public ResourceManager CreateResourceManager()
    {
        if (_resourceManager == null)
            _resourceManager = new ResourceManager("mForum.Resources.ErrorsAndExceptions.ErrorMessages", Assembly.GetExecutingAssembly());

        return _resourceManager;
    }
}
Sam konstruktor ResourceManagera przyjmuje 2 argumenty. 1 to nazwa danej klasy która została wygenerowana wraz z utworzeniem pliku .resx. Drugi to dana biblioteka w której się znajduje ten plik.

Natomiast w widokach przyjąłem troszeczkę inne podejście jeżeli chodzi o zasoby. Zasoby standartowo sa internal czyli dostępne tylko z poziomu danej biblioteki natomiast u mnie wszystkie zasoby zwiazane z warstwa prezentacji będą publiczne co powoduje ze możemy się bezpośrednio odwoływać do pliku .resx z naszych widoków.

Oprócz tych zmian oczywiście dodałem testy związane z naszym ResourceHelperem.

środa, 11 sierpnia 2010

Event Aggregator - Cóż to takiego jest ?

Dziś chciałbym opisać kolejny wzorzec/mechanizm który będę używal i ma on na celu znaczne uproszczenie kodowania i eliminowanie niepotrzebnych powiązań miedzy obiektami.

Event Aggregator
Jak już wcześniej wspomniałem ten wzorzec/mechanizm ma na celu eliminowanie niepotrzebnych powiązań miedzy obiektami. Stanowi on pojedyncze źródło dla wielu obiektów. W najprostszej formie wygląda to tak że rejestrujemy klasy które dotyczą konkretnego zdarzenia do naszego event aggregatora. Te klasy nazywają się uchwytami(handlerami). Natomiast gdy wyślemy zdarzenie do event aggregatora on wybiera wszystkie uchwyty które danego zdarzenia dotyczą, posyła im to zdarzenie a one już zajmują się odbiorem tych zdarzeń i wykonywaniem akcji związanych z nim. W moim forum będę używał dosyć prostego rozwiązania ponieważ nie potrzebuje niczego skomplikowanego. Zdarzenie jest to zwykly obiekt ktory musi implementowac interfejs IEvent. np.
public class UserCreatedEvent : IEvent { }
Nasz event aggregator czyli główny ośrodek tez nie jest skomplikowana klasa:
public class EventAggregator : IEventAggregator
{
    private readonly SynchronizationContext _context;
    private readonly IListenerService _listenerService;

    public EventAggregator(SynchronizationContext context, IListenerService listenerService)
    {
        Check.Argument.IsNotNull(context, "context");
        Check.Argument.IsNotNull(listenerService, "listenerService");
            
        _context = context;
        _listenerService = listenerService;
    }
        
    public void SendMessage<T>(T message) where T : IEvent
    {
        var listeners = _listenerService.GetListeners<T>();

        SendAction(() => listeners.ForEach(x => SendMessageToListeners<T>(x, message)));
    }

    private void SendMessageToListeners<T>(IListenTo<T> x, T message) where T : IEvent
    {
        x.Handle(message);
    }

    private void SendAction(Action action)
    {
        _context.Send(state => action(), null);
    }
}
Sercem calego Event Aggregatora jest funkcja SendMessageToListeners. Ta funkcja odpowiada za rozsyłanie zdarzeń do odbiorcow. Wszystkie uchwyty(handlery) są rejestrowane prze kontener IoC. Natomiast ListenerService poprostu pobiera wszystkie te które dotyczą danego zdarzenia. Następnie za pomocą funkcji lambda Event aggregator już konkretnie wysyła zdarzenia do konkretnych uchwytów. Wszystko jest oczywiście wątkowo-bezpieczne(thread-safe). W razie gdyby miały być jakieś wywołania asynchroniczne. Strasznie to brzmi po polsku :P Event handler to nic innego jak klasa implementujaca intertejs IListenTo ktory wyglada tak:
public interface IListenTo<T> where T : IEvent
{
    void Handle(T message);
}
Jest to zwykla klasa z metoda Handle ktora ma w argumencie dany event.

A teraz praktyczny przykład. Powiedzmy ze tworzymy nowego użytkownika. Z samym utworzeniem wiąże się, nadanie mu podstawowych uprawnień, wysłanie maila potwierdzającego jego adres e-mail. W normalnym wypadku po prostu z danej metody tworzącej wywołalibyśmy metody z innych klas związane z tymi czynnościami. Czyli:
public void CreateUser()
{
    //tutaj akcje związane z dodawaniem użytkownika
    ...

    _rolesService.AddRole();
    _emailService.SendEmail();
}
Powstają niepotrzebne powiązania z rożnymi obiektami. W razie jakiś modyfikacji usług związanych z rolami czy wysyłaniem maili możliwe jest ze będzie potrzeba zmian w wielu miejscach w kodzie. W małych systemach to nie stanowi problemu natomiast w miarę powiększana się utrudnia prace, modernizacje danej aplikacji. To samo zadanie rozwiązane z pomocą naszego Event Handlera. Mamy zdarzenie CreatedUserEvent.
//tutaj mamy oddzielne klasy nie zwiazane w ogole z metoda CreateUser
class VerificationEmailHandler: IListenTo<CreatedUserEvent> { };
class BasicRoleHandler: IListenTo<CreatedUserEvent> { };
//--------------------------------------------------------
public void CreateUser()
{
    //tutaj akcje związane z dodawaniem użytkownika
    ...

    CreatedUserEvent event = new CreatedUserEvent();
    _eventAggregator.SendMessage(testEvent);
}
Odrazu widac ze usunęliśmy zbędne powiązania miedzy obiektami. Metoda CreatedUser nie ma żadnego pojęcia na temat innych usług(servicow) niezwiązanych konkretnie z nią. Poza tym łatwo taki kod rozszerzać o nowe uchwyty w miarę potrzeb gdyż wystarczy zarejestrować nowy uchwyt i zostanie on wykonany. Nie potrzeba żadnej zmiany w kodzie.

Oprócz samego Event Handlera dodałem oczywiście test związany z nim. Co za tym idzie stworzyłem nowy projekt testowy związany z mForum.Core. To tyle na dziś :)

wtorek, 10 sierpnia 2010

Infrastruktura związana ściśle z Asp.net MVC...

Teraz czas przyszedł na podstawowa infrastrukturę związana z Asp.net MVC. Czyli miedzy innymi: ViewData Model, Commands, przystosowanie widoków do nowej struktury. Oprócz tego omówię krótko automappera.

ViewData Model
Cóz to za potworek wielu pewnie zapyta. Są to modele(klasy) używane do przekazywania danych z kontrolera do widoku. Dzięki niemu widoki mogą być silnie typowane na konkretny model. Możemy dzięki temu uniknąć używania ViewData i mamy wszystkie dane z widoku ładnie w jednym modelu. Znacznie ułatwia to dostęp do danych. Nie będę tutaj się za bardzo rozpisywał na ten temat ale znacznie rozbudowane wytłumaczenie możecie znaleźć na tym blogu

U mnie struktura ViewDataModel wygląda tak:
BaseViewData - jest to model z Site.Mastera czyli wszystkie dane które potrzebujemy do głównego szablonu. Miedzy innym meta słowa, tytuł, menu etc. Wszystkie inne ViewData Modele dziedziczą po tym. Implementacja:
public class BaseViewData
{
    public string SiteTitle { get; set; }

    public string PageTitle { get; set; }

    public string MetaKeywords { get; set; }

    public string MetaDescription { get; set; }
}
Następnie jest konkretny ViewDataModel dotyczący kontrolera np. AccountViewData który to w konstruktorze ustawia tytuł strony. Przykladowa implementacja dla kontrolera Account:
public class AccountViewData : BaseViewData
{
    private string _pageTitle = "Account";

    public AccountViewData()
    {
        PageTitle = _pageTitle;
    }
}
A potem już konkretne widoki posiadają Modele które dziedziczą z danego modelu kontrolera np:
public class LogOnViewData : AccountViewData
{
    [Required]
    [DisplayName("User name")]
    public string UserName { get; set; }

    [Required]
    [DataType(DataType.Password)]
    [DisplayName("Password")]
    public string Password { get; set; }

    [DisplayName("Remember me?")]
    public bool RememberMe { get; set; }
}
Generowanie ViewData modeli odbywa się za pomocą fabryki po to aby konkretny kontroler nie musiał wiedzieć on zmiennych z Site.Mastera. Przykładowe utworzenie ViewData modelu dla kontrolera Account widoku logowania
public ActionResult LogOn()
{
    LogOnViewData view = _viewDataFactory.Create<LogOnViewData>();

    return View(view);
}

Commands
Następny element związany z widokami to commandy. Sa one używane do bindowana danych które przesyła nam formularz do konkretnego kontrolera. Dzięki temu możemy napisać
public ActionResult Register(RegisterCommnad command)
zamiast:
public ActionResult Register(FormCollection formitems)
i z tego wyciagac pola.

Automapper
Automapper jest to mala biblioteka ułatwiajaca mapowanie obiektu na inny obiekt. Przydaje sie to głównie w transformacji z encji na DTO. Samo mapowanie po skonfigurowaniu wyglada tak:
User testUser = new testUser(); 
UserDTO testUserDTO = Mapper.Map<User, UserDTO>(testUser);
Jak widać miło, łatwo i przyjemnie. Teraz pare słów o konfiguracji. Zacznijmy od stworzenia profilu czyli klasy ktora posiada informacje w jaki sposob mapowac te obiekty. Wyglada ona tak:
public class DefaultProfile : Profile
{
    public override string ProfileName
    {
        get
        {
            return "DefaultProfile";
        }
    }

    protected override void Configure()
    {
        // tutaj konfigurujemy typy
        // Przyklad z User na UserDTO
        Mapper.CreateMap<User, UserDTO>()
            .ForMember(dest => dest.Id, opt => opt.MapFrom(src => src.Id))
        //podalem tylko pierwsze pole ale reszta analogicznie
    }
}
Następnie musimy utworzyć Bootstrapper task który będzie nam ta konfiguracje wczytywał na początku:
public class AutoMapperConfiguratorTask : BootstrapperTask
{
    protected override TaskContinuation ExecuteCore(IServiceLocator serviceLocator)
    {
        Mapper.Initialize(x => x.AddProfile<DefaultProfile>());

        return TaskContinuation.Continue;
    }
}

I już mamy skonfigurowanego automappera. Jest to bardzo przydatna biblioteczka ułatwiająca życie. Oprócz tych głównych zmian co opisałem we wpisie. Dodałem prosta usługę związana z użytkownikami i odpowiednio zmodyfikowałem kontrolery i widoki. Wiec już proste logowanie działa.

niedziela, 8 sierpnia 2010

Asp.net MVC - IoC i Bootstrapper. Wprowadzenie do biblioteki MVC Extensions

Następnie po interfejsie bazy danych postanowiłem zabrać się za implementowanie Bootstrappera i kontenera IoC. Troszkę opowiem tutaj o wzorcu DI i MVC tytułem wprowadzenia

Dependancy injection i Inversion of Control(IoC)
Dependancy injection(wstrzykiwanie zależności) jest to wzorzec projektowy polegajacy na usuwaniu bezpośrednich zależności miedzy komponentami na rzecz architektury typu plugin. W praktyce to wygląda tak ze stosuje się interfejsy zamiast konkretnych klas co powoduje ze można je bardzo łatwo podmieniać. Poprzez ta technikę możemy tworzyć łatwo testowalne obiekty.

Asp.net MVC
MVC jest architektonicznym wzorcem który rozdziela aplikacje na 3 główne komponenty: Model, Widok i Kontroler. W Asp.net jest on alternatywa do programowania Web Forms.

Model : Jest to komponent który składa się z obiektów biznesowych(encji) jak również logiki biznesowej. Generalnie ta cześć jest odpowiedzialna za wyciąganie i przechowywanie danych w bazie danych.

Widok : Widoki są to komponenty które wyświetlają interfejs użytkownika. Po odpowiednim przetworzeniu kodem wynikowym jest to kod html(w przypadku aplikacji web) i inne z tym związane technologie. Zwykle widoki są generowane w oparci o model danych.

Kontroler : Kontrolery maja za zadanie obsługiwać interakcje z użytkownikiem, pracować z modelem danych i wybierać jaki widok maja wyświetlić.

Po wiecej informacji polecam zajrzeć na stronę Asp.net MVC. Po krótkim wprowadzeniu możemy wrócić do głównego tematu.

Bootstrapper
Coż to takiego jest Bootstraper pewnie zapytacie. To jest obiekt który jest wykonywany na początku aplikacji web. Ma za zadanie tworzyć podstawowe konfiguracje i wykonywać zadania które są niezbędne aby aplikacja mogla zostać uruchomiona np. rejestracja kontenera IoC, skonfigurowanie routingu, dodanie własnej fabryki kontrolerów. Otoż wspomina w tytule biblioteka MVC Extensions bardzo na ułatwia ta czynność ponieważ większość już jest w mniej zawarta. Zacznijmy od początku. Pierwsza czynnością jest zastąpienie obiektu HttpApplication z global.asax odpowiednim z biblioteki Mvc Extensions. W moim wypadku będzie to UnityMvcApplication ponieważ używam kontenera Unity. Global.asax będzie teraz wyglądał tak:
public class MvcApplication : UnityMvcApplication
{

}
Jak widać nic specjalnego. Jednak to powoduje ze nie będziemy musieli np ręcznie tworzyć UnityControllerFactory która jest niezbędna aby nasza aplikacja działa z tym kontenerem. Również ta klasa spowoduje wykonanie zadań podstawowych związanych z Asp.net MVC jak również własnych które możemy zaimplementować. Oprócz tego musimy dodać obiekt który implementuje interfejs IModule. Będzie on służyć do konfiguracji kontenera IoC.
public class RegisterServices : IModule
{
    public void Load(IUnityContainer container)
    {
        //tutaj konfigurujemy kontener. Dodajemy repozytoria,uslugi.
    }
}

Bootstrapper Tasks
Pierwszy zadaniem który jest związany bezpośrednio z Asp.net MVC jest klasa która będzie konfigurowała nam routing. Musi on dziedziczyć po klasie RegisterRoutesBase. Przykładowa implementacja:
public class RegisterRoutes : RegisterRoutesBase
{
    protected override void Register(RouteCollection routes)
    {
        //tutaj zajmujemy sie konfiguracja routingu.
    }
}
Kolejnym ciekawym zadaniem choć nie wymaganym jest klasa która pozwala nam na konfiguracje globalnych filtrów. Krotko mówiąc pozwoli nam to na wstrzykiwanie zależności do filtrów gdyż normalnie nie ma możliwości aby to zrobić. Na temat konkretnego rozwiązania i przykładów będę pisał później. Przykladowa implementacja:
public class RegisterGlobalFilters : ConfigureFiltersBase
{
    protected override void Configure(IFilterRegistry registry)
    {
        //tutaj rejestrujemy globalne filtry
        registry.Register<HomeController, MyCustomAttribute>();
    }
}
Oprócz tych specjalny zadań które muszą być wykonywane przed rozpoczęciem aplikacji możemy definiować własne. Obiekty te muszą dziedziczyć po BootstrapperTask. U mnie takim zadaniem jest np inicjowanie klasy udostępniającej kontener IoC aplikacji:
public class RegisterIoC : BootstrapperTask
{
    protected override TaskContinuation ExecuteCore(IServiceLocator serviceLocator)
    {
        IoC.InitializeWith(serviceLocator);

        return TaskContinuation.Continue;
    }
}
Jedynym niezrozumiałym elementem moze byc tutaj zwracany typ TaskContinuation. Może on przyjąć 3 wartości: Continue, Skip, i Break. Continue powoduje wykonywanie następnych zadań w kolejności normalnie, Skip powoduje ominiecie następnego w kolejności zadania natomiast Break powoduje zaprzestanie wykonywania następnych zadań.

Jak widać biblioteka MVC Extensions bardzo nam ułatwiła życie gdyż posiada wiele ciekawych gotowych rozwiązań. Bardzo polecam zapoznać się z nią. Poza tym wiele rozwiązań z te biblioteki ujrzymy w Asp.net MVC 3.0

Interfejs dostepu do bazy danych ciąg dalszy...

Jak już wcześniej wspominałem następnym krokiem w implementacji interfejsu związanego z baza danych będzie podstawowe repozytorium. Będzie ono posiadała cześć wspólna wszystkich repozytoriów. Czyli np, operacje CRUD(wstawianie, dodawanie,usuwanie,modyfikowanie), pobieranie po id oraz zwracanie elementów z danego przedziału(stronicowanie elementów). Oto implementacja tej klasy:
public class BaseRepository<TEntity> : IRepository<TEntity>, IDisposable where TEntity : class, IEntity
{
    private DatabaseContext _databaseContext;

    public BaseRepository(IDatabaseFactory databaseFactory)
    {
        Check.Argument.IsNotNull(databaseFactory, "databaseFactory");

        DatabaseFactory = databaseFactory;
    }

    protected IDatabaseFactory DatabaseFactory
    {
        get;
        private set;
    }

    protected DatabaseContext DatabaseContext
    {
        get
        {
            return _databaseContext ?? (_databaseContext = DatabaseFactory.Get());
        }
    }

    public void Dispose()
    {
        if (DatabaseContext != null)
            GC.SuppressFinalize(DatabaseContext);

        if (DatabaseFactory != null)
            GC.SuppressFinalize(DatabaseFactory);
    }

    public IEnumerable<TEntity> GetBySpec(ISpecification<TEntity> specification)
    {
        Check.Argument.IsNotNull(specification, "specification");

        IEnumerable<TEntity> result = DatabaseContext.CreateQuery<TEntity>().Where(specification.SatisfiedBy());

        return result;
    }

    public virtual void Add(TEntity entity)
    {
        Check.Argument.IsNotNull(entity, "entity");

        DatabaseContext.Save<TEntity>(entity);
    }

    public virtual void Delete(Guid id)
    {
        Check.Argument.IsNotNull(id, "id");

        TEntity entity = GetById(id);

        if (entity != null)
            DatabaseContext.Delete<TEntity>(entity);
    }

    public virtual void Delete(TEntity entity)
    {
        Check.Argument.IsNotNull(entity, "entity");

        DatabaseContext.Delete<TEntity>(entity);
    }

    public virtual TEntity GetById(Guid id)
    {
        return DatabaseContext.CreateQuery<TEntity>().SingleOrDefault<TEntity>(x => x.Id == id);
    }

    public virtual IEnumerable<TEntity> All()
    {
        return DatabaseContext.CreateQuery<TEntity>();
    }

    public IEnumerable<TEntity> GetPagedElements<S>(int pageIndex, int pageSize, Expression<Func<TEntity, S>> orderExpression, bool ascending)
    {
        Check.Argument.IsNotNegativeOrZero(pageIndex, "pageIndex");
        Check.Argument.IsNotNegative(pageSize, "pageSize");
        Check.Argument.IsNotNull(orderExpression, "orderExpression");

        IQueryable<TEntity> objectQuery = DatabaseContext.CreateQuery<TEntity>();

        return (ascending) ? objectQuery.OrderBy(orderExpression)
                                        .Skip((pageIndex - 1) * pageSize)
                                        .Take(pageSize)
                                        .ToList()

                            : objectQuery.OrderByDescending(orderExpression)
                                        .Skip((pageIndex - 1) * pageSize)
                                        .Take(pageSize)
                                        .ToList();

    }

    public IEnumerable<TEntity> GetPagedElements<S>(int pageIndex, int pageSize, Expression<Func<TEntity, S>> orderExpression, ISpecification<TEntity> specification, bool ascending)
    {
        Check.Argument.IsNotNegativeOrZero(pageIndex, "pageIndex");
        Check.Argument.IsNotNegative(pageSize, "pageSize");
        Check.Argument.IsNotNull(orderExpression, "orderExpression");
        Check.Argument.IsNotNull(specification, "specification");

        IQueryable<TEntity> objectQuery = DatabaseContext.CreateQuery<TEntity>();

        return (ascending) ? objectQuery.Where(specification.SatisfiedBy())
                                        .OrderBy(orderExpression)
                                        .Skip((pageIndex - 1) * pageSize)
                                        .Take(pageSize)
                                        .ToList()

                            : objectQuery.Where(specification.SatisfiedBy())
                                        .OrderByDescending(orderExpression)
                                        .Skip((pageIndex - 1) * pageSize)
                                        .Take(pageSize)
                                        .ToList();

    }
}
Jak widac wiekszosc metod tutaj to proste operacje na bazie danych za pomoca LINQ.
Oprocz tego jest metoda ktora za pomoca wczesniej wspomnianej specyfikacji wyciaga dane z bazy. Teraz przyklad prostego repozytorium uzytkownika:
public class UserRepository : BaseRepository<User>, IUserRepository
{
    public UserRepository(IDatabaseFactory dbFactory)
        : base(dbFactory)
    {

    }

    public int CountAllUsers()
    {
        return DatabaseContext.Users.Count();
    }

    public int CountNumberOfOnlineUsers()
    {
        UserOnlineUsersSpecification specification = new UserOnlineUsersSpecification();

        return this.GetBySpec(specification).Count<User>();
    }

    public IEnumerable<User> GetAllUsersOnline()
    {
        UserOnlineUsersSpecification specification = new UserOnlineUsersSpecification();

        return this.GetBySpec(specification);
    }

    public User FindUserByUsername(string username)
    {
        Check.Argument.IsNotEmpty(username, "username");

        UserUsernameSpecification specification = new UserUsernameSpecification(username);

        return this.GetBySpec(specification).SingleOrDefault<User>();
    }


    public User FindUserByEmail(string email)
    {
        Check.Argument.IsNotEmpty(email, "email");
        Check.Argument.IsNotInvalidEmail(email, "email");

        UserEmailSpecification specification = new UserEmailSpecification(email);

        return this.GetBySpec(specification).SingleOrDefault<User>();
    }
}
W tym przykładzie możemy zobaczyć wykorzystanie już tych tajemniczych specyfikacji. Jak widać to rozwiązanie dosyć fajnie się prezentuje i oddziela logikę od samej bazy danych. W dodatku można ich wielokrotnie używać w rożnych miejscach naszego repozytorium. Dodałem 3 rodzaje specyfikacji do projektu związane z użytkownikiem. Wiec można sobie tez oglądnąć. To repozytorium ich właśnie używa.

W zasadzie cały interfejs dostępu do bazy danych już jest prawie gotowy. Możliwe są ewentualnie małe poprawki. W projekcie testów NHibernate dodałem testy związane z klasami powyżej.

sobota, 7 sierpnia 2010

NHibernate 3.0 i FluentNHibernate pierwsze kroki i pierwsze problemy...

Jako że postanowiłem zastosować do mojej aplikacji NHibernate 3.0 w konsekwencji również chciałem móc używać FluentNhibernate. I tutaj pojawił się mały problem. Ponieważ oficjalnie autorzy nie wspierają jeszcze nowej wersji NHibernate ponieważ jest to wersja alpha. Natomiast gdy chciałem użyć aktualnej wersji to wyrzucał błędy ze wersja biblioteki mu nie odpowiada. Troszeczkę poszperałem w internecie i znalazłem rozwiązanie. Otóż wystarczy ściągnąć źródła FluentNHibernate, następnie w owym projekcie podmienić stare biblioteki na nowe z NHibernate 3.0. Następnie zbudować i wszystko działa.

Jak już uporałem się z wcześniejszym problem napisałem podstawowe klasy obsługi bazy danych takie jak: DatabaseContext i DatabaseFactory. W dużej mierze były one wzorowane na projekcie shrinkr z codeplexa.

Database Context
DatabaseContext zawiera podstawowe metody takie jak zapisywanie, usuwanie obiektów z bazy, zatwierdzanie zmian.
public class DatabaseContext : IDisposable
{
    private readonly ISession _session;
    private ITransaction _transaction;

    private IQueryable<User> _users;

    public DatabaseContext(ISession session)
    {
        Check.Argument.IsNotNull(session, "session");

        _session = session;
    }

    public IQueryable<User> Users
    {
        [DebuggerStepThrough]
        get
        {
            return _users ?? (_users = CreateQuery<User>());
        }
    }

    public virtual IQueryable<TEntity> CreateQuery<TEntity>() where TEntity : class, IEntity
    {
        return _session.Query<TEntity>();
    }

    public virtual TEntity GetById<TEntity>(long id) where TEntity : class, IEntity
    {
        Check.Argument.IsNotNegative(id, "id");
        return _session.Get<TEntity>(id);
    }

    public virtual void Save<TEntity>(TEntity entity) where TEntity : class, IEntity
    {
        Check.Argument.IsNotNull(entity, "entity");
        EnsureTransaction();
        _session.SaveOrUpdate(entity);
    }

    public virtual void Delete<TEntity>(TEntity entity) where TEntity : class, IEntity
    {
        Check.Argument.IsNotNull(entity, "entity");
        EnsureTransaction();
        _session.Delete(entity);
    }

    public virtual void Commit()
    {
        EnsureTransaction();
        _transaction.Commit();
    }

    private void EnsureTransaction()
    {
        if (_transaction == null || !_transaction.IsActive || _transaction.WasCommitted || _transaction.WasRolledBack)
        {
            _transaction = _session.BeginTransaction();
        }
    }
}


Database Factory
Natomiast DatabaseFactory jest głównym obiektem związanym z zarządzaniem baza ponieważ to w nim odbywa się konfiguracja całego Nhibernate za pomoc wcześniej wspomnianej składni Fluent. Jest ona umieszczona w metodzie prywatnej jednak nie będę jej tutaj pokazywał. Interfejs tej klasy posiada tylko 1 metodę z pomocą której możemy otrzymać DatabaseContext.
public interface IDatabaseFactory
{
    DatabaseContext Get();
}

Unit of Work
Ostatnim obiektem związanym z baza który dziś dodałem jest UnitOfWork. Ponieważ większość operacji na danych odbywa się za pomocą transakcji. Musimy mieć obiekt pozwalający nam zatwierdzać zmiany. Po to właśnie ten obiek został stworzony. Interfejs wyglada tak:
public interface IUnitOfWork
{
    void Commit();
}
Po implementacje samej klasy zapraszam do źródeł.

Acha i jeszcze dodałem do solucji projekt testowy związany z Nhibernatem. To tyle na dziś. W kolejnym wpisie będzie co nieco na temat implementacji bazowych repozytoriów i pewnie wstepna implementacja repozytorium użytkownika.

Projektowanie interfejsu zwiazanego z baza danych.

Dzisiaj zajmiemy się projektowaniem podstawowych interfejsów związanych z logika biznesowa i baza danych. Miedzy innymi implementacja encji, interfejsu repozytorium.

Encje
Zacznijmy od tworzenia encji. Na potrzeby budowania architektury aplikacji narazie stworze tylko 1 encje. Będzie nią użytkownik. Zacznijmy od zaimplementowania interfejsu encji. Częścią wspólna każdej encji jest id. Więc nasz interfejs będzie posiadał właśnie to pole.
public interface IEntity
{
    Guid Id { get; set; }
}
Aby ułatwić sobie sprawę stworzymy bazowa encje która będzie implementowała powyższy interfejs. Spowoduje to ze nie będziemy musieć w każdej encji wypisywać pola id. W razie gdyby pojawiły się inne pola które będą wspólne równie łatwo będzie można modyfikowac bazowa encje co spowoduje zmiany w wszystkich encjach.
Oto implementacja bazowej encji:
public abstract class BaseEntity : IEntity
{
    public virtual Guid Id { get; set; }
}

Jak juz mamy podstawowe typy z których będą dziedziczyły nasze encje możemy teraz zaimplementować pierwsza z nich:
public class User : BaseEntity, IEntity
{        
    public virtual string Username { get; set; }

    public virtual string Password { get; set; }

    public virtual string Email { get; set; }

    public virtual string PasswordQuestion { get; set; }

    public virtual string PasswordAnswer { get; set; }

    public virtual bool isLockedOut { get; set; }

    public virtual DateTime LastActivityDate { get; set; }

    public virtual DateTime LastLoginDate { get; set; }

    public virtual DateTime LastLockedOutDate { get; set; }

    public virtual DateTime CreatedDate { get; set; }

}

Repozytoria
Następnie chciałbym co nieco powiedzieć na temat obiektów które będzie można znaleźć w moim projekcie. Są to repozytoria. Repozytorium jest to klasa która bezpośrednio ma dostęp do bazy danych. Udostępnia on podstawowe operacje CRUD jak i inne bezpośrednio związane z dana encja. Pośredniczy miedzy baza a usługami które te dane będą potrzebowały. Pozwala to na uniezależniane projektu od konkretnego ORM-a. Podmiana wymaga jedynie zaimplementowania kontekstu bazy danych oraz poszczególnych repozytoriów. Implementacja interfejsu repozytorium wygląda tak:
public interface IRepository<TEntity> where TEntity : class
{
    TEntity GetById(Guid id);
    IEnumerable<TEntity> GetBySpec(ISpecification<TEntity> spec);
    void Add(TEntity entity);
    void Delete(Guid id);
    void Delete(TEntity entity);
    IEnumerable<TEntity> GetPagedElements<S>(int pageIndex, int pageSize, Expression<Func<TEntity, S>> orderExpression, bool ascending);
    IEnumerable<TEntity> GetPagedElements<S>(int pageIndex, int pageSize, Expression<Func<TEntity, S>> orderExpression, ISpecification<TEntity> specification, bool ascending);
    IEnumerable<TEntity> All();
}
Większość funkcji tutaj jest raczej zrozumiała. Jedyna zagadka możne być ISpecifcation interfejs. Spójrzmy na przykładowa implementacje:
public class UserOnlineUsersSpecification : Specification<User>
{
    private DateTime _onlineTime;

    public UserOnlineUsersSpecification()
    {
           
    }

    public override Expression<Func<User, bool>> SatisfiedBy()
    {
        _onlineTime = DateTime.Now.AddMinutes(-5);

        return x => x.LastActivityDate >= _onlineTime;
    }
}
Ta specyfikacja ma za zadanie zwrócić wyrażenie lamba które pozwoli nam znaleźć aktualnie użytkowników online. Jak widzymy kazda specyfikacja sklada sie metody SatisfiedBy która zwraca wyrażenie lamba. To wyrażenie jest wykorzystywane w bazie do pobrania odpowiednich danych. Krótko mówiac jest to mała cześć logiki biznesowej opakowana w klase co powoduje że może byc używana w wielu miejscach i unikamy powtórzeń w kodzie. Wiecej na temat tego wzorca mozna znaleść na Wikipedi

W nastepnych wpisach zajmiemy sie juz konkretna implementacja klas zwiazanych z NHibernate i baza danych.

piątek, 6 sierpnia 2010

Struktura projektu oraz podstawowe klasy pomocnicze i rozszerzenia.

W tym poście postaram się przybliżyć główna strukturę projektu jak również opisać podstawowe klasy pomocnicze i rozszerzenia które używam w większości moich projektów.

Struktura Projektu:
- mForum.Core - Tutaj będą znajdować się wszystkie klasy/struktury/metody które są wspólne dla całej solucji i będą używane w każdym z projektów.
- mForum.Domain - W tym projekcie będzie znajdować się wszystko co związane z logika biznesowa czyli np. encje, specyfikacje.
- mForum.Infrasturcture.Automapper - W tym projekcie będą klasy potrzebne do zainicjowana biblioteki AutoMapper
- mForum.Infrastructure.NHibernate - Tutaj znowuż znajdziemy wszystko co związane z mapowaniem danych i baza. Czyli implementacje repozytoriów, obiekty inicjujące baze danych.
- mForum.Services - Jest to warstwa pośrednicząca miedzy logika biznesowa a warstwa prezentacji która jest Asp.net MVC w tym projekcie. Znajdziemy tutaj glownie wszystkie klasy, metody które będą pośredniczyły w obiegu danych miedzy warstwa logiki biznesowej a warstwa prezentacji.
- mForum.Web - Tutaj mamy warstwę prezentacji. W tym projekcie znajdziemy widoki, skrypty js, szablony css. Generalnie wszystko co związane z projektowanie stron www od strony graficznej.
- mForum.Web.Common - Jest to projekt w którym są obiekty bezpośrednio związane z warstwa prezentacji od strony programistycznej. Będziemy tutaj mogli znaleźć własne atrybuty, kontrolery mvc, zadania które będą wykonywane tuz przed rozpoczęciem aplikacji(tzw. bootstrapper taski).

Jak wspominałem wcześniej dodałem już klasy/metody które używam zwykle w każdym projekcie, takie klasy uniwersalne wspomagające prace.
Pierwsza z nich jest klasa Check. Ta klasa posiada metody sprawdzajace dane wejsciowe i w wypadku niezgodnosci warunku w danej metodzie wyrzuca odpowiedni wyjatek. Prosty przyklad sprawdzania czy dane wejsciowe nie sa null:
void testMethod(object testProperty)
{
    if(testProperty == null)
        throw new ArgumentException("testProperty cannot be null: ");
    ...
}
W wypadku pojedynczego argumenty nie jest to zbyt uciążliwe natomiast przy ich większej ilości może powodować lekkie nagromadzenie się instrukcji warunkowych. To samo rozwiązane za pomocą klasy Check:
void testMethod(object testProperty)
{
    Check.Argument.IsNotNull(testProperty, "testProperty");
    ...
}
Klasa Check posiada znacznie więcej metod walidacji danych wejściowych. Po więcej zapraszam do zapoznania sie z kodem.

Oprócz tego dodałem parę metod rozszerzających do klas:
- DateTime: metoda sprawdzająca poprawność daty. Klasa Check bezpośrednio korzysta z tych metod.
- String: metody takie jak sprawdzanie poprawności danego łańcucha znaków jako adres url czy adres e-mail. Klasa Check również z nich korzysta.
- IEnumerable: Dodałem rozszerzenie które pozwala w 1 linijce kodu wykonać iteracje po wszystkich elementach kolekcji i wykonanie jakieś akcji na nich. Petla foreach w skondensowanej formie.

To tyle na dzisiaj. Nadchodzi weekend wiec prawdopodobnie można spodziewać się większej ilości wpisów. Acha i jeszcze jedno właśnie opublikowałem projekt na codeplex wiec już każdy będzie miał dostęp do niego. Co prawda niewiele tam jeszcze jest ale coż :)

środa, 4 sierpnia 2010

Kilka słów na temat planowania aplikacji i wybranych technologii.

Ja już wcześniej wspominałem będę tworzył rozbudowany system forum. Można spytać dlaczego akurat forum skoro jest ich pełno? Głównie dlatego że już od dosyć dawna miałem w planach napisanie właśnie takiego projektu ale jakoś nie potrafiłem się zebrać i zacząć pisać. Konkurs był właśnie takim impulsem do rozpoczęcia.

Główne środowisko w którym będę pisał:
- Visual Web Developer 2010 Express
- Microsoft Sql Server 2008 Express

Technologie wykorzystane:
Kolejna rzeczą jaka bym chciał poruszyć to kilka słów na temat wybranych technologi do tworzenia tego projektu:
- ASP.NET MVC 2 : Tutaj sprawa była dosyć prosta ponieważ zdecydowałem się napisać ten projekt na platformie .NET to miałem do wyboru ASP.NET Webforms i właśnie MVC. Osobiście nigdy nie przypadło mi do gustu styl i sposób programowania w webforms. Wiele problemów powodował np brak bezpośredniego się wpływu na generowany kod, utrudniona mozliwosci stosowania wzorcow projektowych co powodowalo trudniejsze testowanie aplikacji.
- NHibernate : Przejdźmy teraz do kwestii ORM-ów. Brałem pod uwagę 2 NHibernate i Entity Framework. Po dogłębnym przeanalizowaniu ich możliwości i sposobu tworzenia w nim projektów uważam ze Entity Framework nie dorównuje NHibernate w aktualnej wersji. Natomiast ostatnio zaczęły pojawiać się wersje CTP EF-a tzw Code-First i muszę przyznać ze zapowiada się dosyć dobrze. Możliwe ze przyszłe wersje dorownaja NHibernate. Jedynym problemem w Nhibernate jest słabszy LINQ provider niz w EF aczkolwiek mam zamiar uzyc do tego projektu wersji alpha Nhibernate która niedawno się ukazała. W tej powinien być poprawiony. Będzie co testowac :)
- Automapper : Tutaj to niema co dyskutować. Nic bardziej nie ułatwia życia programisty jak automatyczne mapowanie np. z obiektow DTO na obiekty domenowe. Do tego celu służy właśnie ta biblioteka.
- Microsoft Unity Container : Jako mój kontener IoC wybrałem Unity. Głównie z powodu żę dosyć długo już go używam i odpowiada mi sposób jego konfiguracji i prostota używania.
- MVC Extensions : W tej bibliotece znajduje się wiele przydanych narzędzi które mogą ułatwić/pomoc w projektowaniu aplikacji jak np. Bootstrapping, Globalne filtry etc. Więcej na ten temat pewnie będę opisywał w czasie projektowania aplikacji.

Ta lista może systematycznie się powiększać w miarę rozwoju projektu. Na razie to tyle. W następnym wpisie postaram się już przybliżyć ogólna strukturę projektu i oficjalnie opublikować źródła(na razie głownie puste:)) na codeplex.

wtorek, 3 sierpnia 2010

Powoli zaczynamy...

Witam na blogu projektu programistycznego na konkurs "Daj się poznać" Macieja Ansierowicza. Po wiecej informacji odsyłam na strone konkursu

Na tym blogu będę publikował poczynania związane z rozwojem projektu "mForum". Jest to projekt rozbudowanego systemu forumowego. Będzie on pisany w C# z wykorzystaniem Asp.net MVC.

To tyle jeżeli chodzi o pierwszy wpis. Za kilka dni na dobre postaram sie rozpocząć prace nad projektem :)