Oto artykuł Jona Davisa. Aby zachować czytelność, wycinam przestarzałą sekcję EntLib, wprowadzenie oraz zakończenie.
Pamięć podręczna ASP.NET
ASP.NET lub zestaw System.Web.dll ma mechanizm buforowania. Nigdy nie był przeznaczony do użytku poza kontekstem sieciowym, ale może być używany poza siecią i wykonuje wszystkie powyższe zachowania związane z wygaśnięciem w pewnego rodzaju tablicy haszującej.
Po przeszukaniu Google okazuje się, że sporo osób, które dyskutowały o wbudowanej funkcji buforowania w .NET, uciekło się do korzystania z pamięci podręcznej ASP.NET w swoich projektach innych niż internetowe. Nie jest to już najbardziej dostępny i obsługiwany wbudowany system buforowania w .NET; NET 4 ma ObjectCache, do którego przejdę później. Firma Microsoft zawsze była nieugięta, że pamięć podręczna ASP.NET nie jest przeznaczona do użytku poza siecią. Ale wiele osób wciąż tkwi w .NET 2.0 i .NET 3.5 i potrzebuje czegoś do pracy, a to działa dla wielu ludzi, mimo że MSDN mówi wyraźnie:
Uwaga: Klasa Cache nie jest przeznaczona do użytku poza aplikacjami ASP.NET. Został zaprojektowany i przetestowany do użytku w ASP.NET w celu zapewnienia buforowania dla aplikacji sieci Web. W innych typach aplikacji, takich jak aplikacje konsolowe lub aplikacje Windows Forms, buforowanie ASP.NET może nie działać poprawnie.
Klasa pamięci podręcznej ASP.NET to System.Web.Caching.Cache w System.Web.dll. Nie można jednak po prostu utworzyć nowego obiektu pamięci podręcznej. Musisz go pobrać z System.Web.HttpRuntime.Cache.
Cache cache = System.Web.HttpRuntime.Cache;
Praca z pamięcią podręczną ASP.NET jest udokumentowana w witrynie MSDN tutaj .
Plusy:
- Jest wbudowany .
- Pomimo składni .NET 1.0 jest dość prosty w użyciu.
- Używany w kontekście sieciowym jest dobrze przetestowany . Poza kontekstami internetowymi, według wyszukiwań Google, nie jest powszechnie znane z tego, że powoduje problemy, pomimo rekomendacji firmy Microsoft, o ile używasz .NET 2.0 lub nowszego.
- Możesz otrzymać powiadomienie za pośrednictwem pełnomocnika, gdy element zostanie usunięty, co jest konieczne, jeśli chcesz go utrzymać przy życiu i nie możesz z góry ustawić priorytetu elementu.
- Poszczególne elementy mają elastyczność dowolnej z (a), (b) lub (c) metod wygaśnięcia i usunięcia na liście metod usuwania na początku tego artykułu. Możesz również powiązać zachowanie związane z wygasaniem z obecnością pliku fizycznego.
Cons:
- Nie tylko jest statyczny, jest tylko jeden . Nie można utworzyć własnego typu z własną statyczną instancją pamięci podręcznej. Możesz mieć tylko jeden zasobnik dla całej aplikacji, kropka. Możesz owinąć zasobnik własnymi opakowaniami, które wykonują takie rzeczy, jak prefiksy przed wstrzyknięciem w kluczach i usuwają te prefiksy, gdy wyciągasz pary klucz / wartość z powrotem. Ale wciąż jest tylko jedno wiadro. Wszystko jest zebrane razem. Może to być naprawdę uciążliwe, jeśli na przykład masz usługę, która wymaga osobnego buforowania trzech lub czterech różnych rodzajów danych. Nie powinno to stanowić dużego problemu dla żałośnie prostych projektów. Ale jeśli projekt ma znaczny stopień złożoności ze względu na jego wymagania, pamięć podręczna ASP.NET zwykle nie wystarcza.
- Przedmioty mogą zniknąć, chcąc nie chcąc. Wiele osób nie jest tego świadomych - nie wiedziałem, dopóki nie odświeżyłem swojej wiedzy na temat implementacji tej pamięci podręcznej. Domyślnie pamięć podręczna ASP.NET jest zaprojektowana do niszczenia elementów, gdy „wydaje się” na to ochotę. Dokładniej, zobacz (c) w mojej definicji tabeli pamięci podręcznej na początku tego artykułu. Jeśli inny wątek w tym samym procesie pracuje na czymś zupełnie innym i zrzuca elementy o wysokim priorytecie do pamięci podręcznej, to gdy tylko .NET zdecyduje, że potrzebuje trochę pamięci, zacznie niszczyć niektóre elementy w pamięci podręcznej zgodnie z ich priorytety, najpierw niższe priorytety. Wszystkie udokumentowane tutaj przykłady dodawania elementów pamięci podręcznej używają domyślnego priorytetu, a nie wartości priorytetu NotRemovable, która zapobiega jego usunięciu w celu wyczyszczenia pamięci, ale nadal będzie usuwać go zgodnie z zasadami wygasania.
- Klucz musi być ciągiem. Jeśli na przykład buforujesz rekordy danych, w których rekordy są wpisywane jako długie lub całkowite, musisz najpierw przekonwertować klucz na ciąg.
- Składnia jest nieaktualna . To składnia .NET 1.0, brzydsza nawet niż ArrayList czy Hashtable. Nie ma tutaj żadnych typów ogólnych, nie ma interfejsu IDictionary <>. Nie ma metody Contains (), kolekcji Keys, żadnych standardowych zdarzeń; ma tylko metodę Get () oraz indeksator, który robi to samo co Get (), zwracając wartość null, jeśli nie ma dopasowania, a także Add (), Insert () (redundant?), Remove () i GetEnumerator () .
- Ignoruje zasadę SUCHY podczas konfigurowania domyślnych zachowań związanych z wygasaniem / usuwaniem, abyś mógł o nich zapomnieć. Musisz wyraźnie powiedzieć pamięci podręcznej, w jaki sposób chcesz, aby dodawany element wygasał lub był usuwany za każdym razem, gdy dodajesz element.
- Brak możliwości uzyskania dostępu do szczegółów buforowania elementu w pamięci podręcznej, takich jak znacznik czasu jego dodania. Hermetyzacja poszła tutaj trochę za daleko, utrudniając korzystanie z pamięci podręcznej, gdy w kodzie próbujesz określić, czy buforowany element powinien zostać unieważniony względem innego mechanizmu buforowania (np. Zbierania sesji), czy nie.
- Zdarzenia usunięcia nie są ujawniane jako zdarzenia i należy je śledzić w momencie dodawania.
- A jeśli nie powiedziałem tego wystarczająco dużo, firma Microsoft wyraźnie odradza to poza siecią. A jeśli jesteś przeklęty z .NET 1.1, nie powinieneś używać go z pewnością stabilności poza siecią, więc nie przejmuj się.
.NET 4.0's ObjectCache / MemoryCache
Firma Microsoft ostatecznie zaimplementowała abstrakcyjną klasę ObjectCache w najnowszej wersji platformy .NET Framework oraz implementację MemoryCache, która dziedziczy i implementuje ObjectCache na potrzeby pamięci w ustawieniach innych niż internetowe.
System.Runtime.Caching.ObjectCache znajduje się w zestawie System.Runtime.Caching.dll. Jest to klasa abstrakcyjna, która deklaruje zasadniczo te same interfejsy w stylu .NET 1.0, które można znaleźć w pamięci podręcznej ASP.NET. System.Runtime.Caching.MemoryCache
jest implementacją ObjectCache w pamięci i jest bardzo podobna do pamięci podręcznej ASP.NET, z kilkoma zmianami.
Aby dodać element z ruchomym wygaśnięciem, Twój kod wyglądałby mniej więcej tak:
var config = new NameValueCollection();
var cache = new MemoryCache("myMemCache", config);
cache.Add(new CacheItem("a", "b"),
new CacheItemPolicy
{
Priority = CacheItemPriority.NotRemovable,
SlidingExpiration=TimeSpan.FromMinutes(30)
});
Plusy:
- Jest wbudowany, a teraz obsługiwany i zalecany przez firmę Microsoft poza siecią.
W przeciwieństwie do pamięci podręcznej ASP.NET można utworzyć wystąpienie obiektu MemoryCache.
Uwaga: nie musi to być statyczne, ale powinno być - takie jest zalecenie firmy Microsoft (patrz żółte Ostrzeżenie) .
W porównaniu z interfejsem pamięci podręcznej ASP.NET wprowadzono kilka drobnych ulepszeń, takich jak możliwość subskrybowania zdarzeń usuwania bez konieczności przebywania tam, gdy elementy zostały dodane, usunięto nadmiarową metodę Insert (), elementy można dodać za pomocą elementu CacheItem obiekt z inicjatorem, który definiuje strategię buforowania, i dodano Contains ().
Cons:
- Nadal nie wzmacnia w pełni DRY. Z mojego niewielkiego doświadczenia wynika, że nadal nie możesz raz ustawić ruchomego czasu wygaśnięcia TimeSpan i zapomnieć o tym. I szczerze mówiąc, chociaż zasada w powyższym przykładzie dodawania elementów jest bardziej czytelna, wymaga przerażającej szczegółowości.
- Nadal nie ma klucza ogólnego; wymaga łańcucha jako klucza. Więc nie możesz przechowywać as long lub int, jeśli buforujesz rekordy danych, chyba że przekonwertujesz na ciąg.
Zrób to sam: Zbuduj siebie
W rzeczywistości utworzenie słownika buforującego, który wykonuje jawne lub przesuwane wygaśnięcie, jest całkiem proste. (Jest to o wiele trudniejsze, jeśli chcesz, aby elementy były automatycznie usuwane w celu wyczyszczenia pamięci). Oto wszystko, co musisz zrobić:
- Utwórz klasę kontenera wartości o nazwie coś takiego jak Expiring lub Expiable, która zawierałaby wartość typu T, właściwość TimeStamp typu DateTime do przechowywania, gdy wartość została dodana do pamięci podręcznej, oraz TimeSpan, który wskazywałby, jak daleko od sygnatury czasowej, która pozycja powinna wygasnąć. W przypadku jawnego wygaśnięcia można po prostu ujawnić metodę ustawiającą właściwość, która ustawia TimeSpan z podaną datą odjętą przez znacznik czasu.
- Utwórz klasę, nazwijmy ją ExpiableItemsDictionary, która implementuje IDictionary. Wolę, aby była to klasa ogólna ze zdefiniowaną przez konsumenta.
- W klasie utworzonej w # 2 dodaj Dictionary> jako właściwość i nazwij ją InnerDictionary.
- Implementacja, jeśli IDictionary w klasie utworzonej w # 2 powinna używać InnerDictionary do przechowywania elementów w pamięci podręcznej. Hermetyzacja spowodowałaby ukrycie szczegółów metody buforowania za pośrednictwem instancji typu utworzonego w punkcie 1 powyżej.
- Upewnij się, że indeksator (this []), ContainsKey () itp. Ostrożnie usuwa elementy, które utraciły ważność, i usuwa je przed zwróceniem wartości. Zwróć wartość null w metodach pobierających, jeśli element został usunięty.
- Użyj blokad wątków na wszystkich pobierających, ustawiających, ContainsKey (), a szczególnie podczas czyszczenia wygasłych elementów.
- Podnieś wydarzenie, gdy przedmiot zostanie usunięty z powodu wygaśnięcia.
- Dodaj instancję System.Threading.Timer i ustaw ją podczas inicjalizacji, aby automatycznie usuwała wygasłe elementy co 15 sekund. To jest to samo zachowanie, co pamięć podręczna ASP.NET.
- Możesz chcieć dodać procedurę AddOrUpdate (), która wypycha przesuwający się termin wygaśnięcia, zastępując znacznik czasu w kontenerze elementu (instancja tracąca ważność), jeśli już istnieje.
Microsoft musi wspierać swoje oryginalne projekty, ponieważ jego baza użytkowników jest od nich zależna, ale to nie znaczy, że są to dobre projekty.
Plusy:
- Masz pełną kontrolę nad wdrożeniem.
- Może wzmocnić DRY , konfigurując domyślne zachowanie buforowania, a następnie po prostu upuszczając pary klucz / wartość bez deklarowania szczegółów buforowania za każdym razem, gdy dodajesz element.
- Potrafi implementować nowoczesne interfejsy , a mianowicie
IDictionary<K,T>
. Dzięki temu jest znacznie łatwiejszy w użyciu, ponieważ jego interfejs jest bardziej przewidywalny jako interfejs słownika, a ponadto czyni go bardziej dostępnym dla pomocników i metod rozszerzających, które współpracują z IDictionary <>.
- Szczegóły buforowania mogą być niezamknięte , na przykład przez ujawnienie InnerDictionary przez publiczną właściwość tylko do odczytu, co pozwala na pisanie jawnych testów jednostkowych w odniesieniu do strategii buforowania, a także rozszerzenie podstawowej implementacji buforowania o dodatkowe strategie buforowania, które na niej bazują.
- Chociaż niekoniecznie jest to znajomy interfejs dla tych, którzy już przyzwyczaili się do składni pamięci podręcznej ASP.NET lub bloku aplikacji buforującej w stylu .NET 1.0, można zdefiniować interfejs tak, aby wyglądał tak, jak chcesz.
- Może używać dowolnego typu kluczy. To jeden z powodów, dla których stworzono leki generyczne. Nie wszystko powinno być kluczowane za pomocą sznurka.
Cons:
- Nie został wymyślony przez firmę Microsoft ani przez nią zatwierdzony , więc nie będzie miał takiej samej gwarancji jakości.
- Zakładając, że tylko instrukcje, które opisałem powyżej, są zaimplementowane, nie usuwa "chcąc nie chcąc" pozycji do czyszczenia pamięci na zasadzie priorytetu (co i tak jest funkcją narzędziową w rogu pamięci podręcznej. KUP RAM, w której używałbyś pamięci podręcznej) RAM jest tani).
Spośród wszystkich czterech opcji preferuję to. Zaimplementowałem to podstawowe rozwiązanie buforowania. Jak dotąd wydaje się, że działa idealnie, nie ma żadnych znanych błędów (proszę o kontakt z komentarzami poniżej lub na jon-at-jondavis, jeśli są !!) i zamierzam używać go we wszystkich moich mniejszych projektach pobocznych, które wymagają podstawowe buforowanie. Oto ona:
Link do Github: https://github.com/kroimon/ExpiableItemDictionary
Stary link: ExpiableItemDictionary.zip
Godny uwagi: AppFabric, NoSQL, Et Al
Zwróć uwagę, że tytuł tego artykułu na blogu wskazuje „Proste buforowanie”, a nie „Pamięć podręczna o dużym obciążeniu”. Jeśli chcesz zająć się trudnymi sprawami, powinieneś przyjrzeć się dedykowanym rozwiązaniom skalowalnym.