W .NET 4.0 dodano (mało chyba popularną) klasę MemoryCache służącą do buforowania danych. Wcześniej programiści znali podobną klasę w ASP.NET. Od wersji 4.0 została ona wydzielona i można z niej teraz korzystać we wszystkich typach aplikacji – także WPF i WinForms. Za pomocą MemoryCache można ustawić czas ważności danych w buforze za pomocą czasu absolutnego, priorytetu, monitorów, które unieważniają dane np. na podstawie zmiany pliku na dysku lub tzw. SlidingExpiration – czasu który upłynął od ostatniego odpytania bufora.
Same API jest bardzo proste. Na przykład aby dodać element do bufora wystarczy:
CacheItemPolicy cachePolicy = new CacheItemPolicy(); cachePolicy.AbsoluteExpiration = new DateTimeOffset(new DateTime(2012, 11, 1)); MemoryCache.Default.Add("klucz", "dowolna wartosc", cachePolicy);
Z kolei aby odczytać z bufora wartość:
string value = MemoryCache.Default.Get("klucz").ToString();
Kilka wyjaśnień:
-
AbsoluteExpiration – oznacza kiedy dana wartość zostanie skasowana z bufora (absolutny czas ważności).
-
MemoryCache.Default zwraca domyślny bufor – zwykłe aplikacja potrzebuje tylko jednego MemoeryCache i nie ma potrzeby tworzenia własnych obiektów MemoeryCache.
-
Gdy bufor straci ważność metoda Get zwraca NULL – stąd powyższy kod jest niebezpieczny ponieważ może wywołać wyjątek null reference przez ToString().
Oprócz absolutnego czasu ważności możemy zdefiniować wspomniany SlidingException:
CacheItemPolicy cachePolicy = new CacheItemPolicy(); cachePolicy.SlidingExpiration = new TimeSpan(0, 1, 0);
Powyższy kod oznacza, że jeśli wpis w buforze nie będzie wykorzystany (poprzez metodę Get) ani razu w ciągu minuty to zostanie unieważniony.
Do dyspozycji mamy również kilka różnych monitorów:
List<string> paths=new List<string>(); paths.Add("c:\\1.txt"); HostFileChangeMonitor fileChangeMonitor=new HostFileChangeMonitor(paths); cachePolicy.ChangeMonitors.Add(fileChangeMonitor);
HostFileChangeMonitor sprawdza czy przekazane przez parametr paths, pliki lub foldery nie zostały zmienione. Monitorowane są m.in. nazwy plików (lub katalogów), rozmiar, ACL czy pliki w danym katalogu. Ponadto do despocji mamy jeszcze następujący monitory:
- CacheEntryChangeMonitor – unieważnia wpis w przypadku gdy jakiś inny wpis zostały zmieniony.
- SqlChangeMonitor – monitorowanie bazy SQL Server.
Do dyspozycji jest również priorytet, który aktualnie przyjmuje dwie wartości (wydaje mi się, że nazwy mówią same za siebie):
cachePolicy.Priority = CacheItemPriority.Default; cachePolicy.Priority = CacheItemPriority.NotRemovable;
Oprócz poszczególnych elementów w buforze, można również konfigurować bufor jako całość. Domyślny bufor (zwrócony poprzez właściwość Default) można wyłącznie skonfigurować za pomocą XML (np. w App.config dla aplikacji desktop):
<configuration> <system.runtime.caching> <memoryCache> <namedCaches> <add name="default" cacheMemoryLimitMegabytes="0" physicalMemoryPercentage="0" pollingInterval="00:02:00" /> </namedCaches> </memoryCache> </system.runtime.caching> </configuration>
Własne bufory można konfigurować zarówno z poziomu pliku XML jak i konstruktora.
Jak nigdy, tym razem napiszę komentarz gloryfikujący idee powstania wpisu. Sam z MemoryCache nie korzystałem, słyszałem że jest ale nie miałem motywacji by się z nim zapoznać. Po przeczytaniu Twojego wpisu rozwiązaniem jest zafascynowany i z pewnością do niego wrócę.
Świetny wpis. Rzeczowo i bez “polityki” 🙂
@Grzegorz.W
Fajnie, że mogłem pomóc:)
Hej,
Czy próbowałeś uzywać MemoryCache.UpdateCallback ?
Dostaję dziwny błąd i nie mogę znaleźć nic w dokumentacji:
“CacheItemUpdateCallback must be null”
Czy da się stosować cache w ramach konkretnego użytkownika czy to działa globalnie na całą aplikację? Powiedzmy dla danego użytkownika tworzona jest specyficzna strona wzorcowa i chciałabym ją buforować. A jeżeli się da to czy to nie wykończy serwera, jeżeli jest dużo użytkowników? Gdzieś wyczytałam że to jest zapisane na serwerze.