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.

Brak komentarzy:

Prześlij komentarz