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.

1 komentarz:

dario-g pisze...

A możesz także użyć mscd.codeplex.com :)

Prześlij komentarz