Bagaimana cara menghapus MemoryCache?

100

Saya telah membuat cache menggunakan kelas MemoryCache. Saya menambahkan beberapa item ke dalamnya tetapi ketika saya perlu memuat ulang cache saya ingin menghapusnya terlebih dahulu. Apa cara tercepat untuk melakukan ini? Haruskah saya mengulang semua item dan menghapusnya satu per satu atau adakah cara yang lebih baik?

Retrocoder
sumber
1
Untuk .NET core periksa jawaban ini .
Makla

Jawaban:

61

Dispose MemoryCache yang ada dan buat objek MemoryCache baru.

GvS
sumber
3
Saya awalnya menggunakan MemoryCache.Default, menyebabkan Buang membuat saya sedih. Tetap saja, Buang akhirnya menjadi solusi terbaik yang dapat saya temukan. Terima kasih.
LaustN
11
@LaustN dapatkah Anda menjelaskan tentang "kesedihan" yang disebabkan oleh MemoryCache.Default? Saat ini saya menggunakan MemoryCache.Default ... Dokumentasi MemoryCache MSDN membuat saya bertanya-tanya apakah membuang dan membuat ulang disarankan: "Jangan buat instance MemoryCache kecuali diperlukan. Jika Anda membuat instance cache di klien dan aplikasi Web, instance MemoryCache harus dibuat di awal siklus hidup aplikasi. " Apakah ini berlaku untuk .Default? Saya tidak mengatakan bahwa menggunakan Buang itu salah, sejujurnya saya hanya mencari klarifikasi tentang semua ini.
ElonU Webdev
8
Pikir itu layak disebutkan yang Dispose memang memanggil CacheEntryRemovedCallbackitem yang dilampirkan ke cache saat ini.
Mike Guthrie
8
@ElonU: Jawaban Stack Overflow berikut menjelaskan beberapa kesedihan yang mungkin Anda alami saat membuang contoh default: stackoverflow.com/a/8043556/216440 . Mengutip: "Status cache disetel untuk menunjukkan bahwa cache telah dibuang. Upaya apa pun untuk memanggil metode cache publik yang mengubah status cache, seperti metode yang menambah, menghapus, atau mengambil entri cache, dapat menyebabkan perilaku. Misalnya, jika Anda memanggil metode Set setelah cache dibuang, kesalahan tanpa operasi akan terjadi. "
Simon Tewsi
56

Masalah dengan pencacahan

Bagian MemoryCache.GetEnumerator () Keterangan memperingatkan: "Mengambil enumerator untuk instance MemoryCache adalah operasi pemblokiran dan sumber daya intensif. Oleh karena itu, enumerator tidak boleh digunakan dalam aplikasi produksi."

Inilah alasannya , dijelaskan dalam pseudocode implementasi GetEnumerator ():

Create a new Dictionary object (let's call it AllCache)
For Each per-processor segment in the cache (one Dictionary object per processor)
{
    Lock the segment/Dictionary (using lock construct)
    Iterate through the segment/Dictionary and add each name/value pair one-by-one
       to the AllCache Dictionary (using references to the original MemoryCacheKey
       and MemoryCacheEntry objects)
}
Create and return an enumerator on the AllCache Dictionary

Karena implementasi membagi cache di beberapa objek Dictionary, itu harus menyatukan semuanya menjadi satu koleksi untuk mengembalikan enumerator. Setiap panggilan ke GetEnumerator menjalankan proses penyalinan lengkap yang dijelaskan di atas. Dictionary yang baru dibuat berisi referensi ke kunci internal asli dan objek nilai, sehingga nilai data cache Anda yang sebenarnya tidak diduplikasi.

Peringatan di dokumentasi benar. Hindari GetEnumerator () - termasuk semua jawaban di atas yang menggunakan kueri LINQ.

Solusi yang lebih baik dan lebih fleksibel

Berikut adalah cara efisien untuk membersihkan cache yang hanya dibangun di atas infrastruktur pemantauan perubahan yang ada. Ini juga memberikan fleksibilitas untuk menghapus seluruh cache atau hanya subset bernama dan tidak memiliki masalah yang dibahas di atas.

// By Thomas F. Abraham (http://www.tfabraham.com)
namespace CacheTest
{
    using System;
    using System.Diagnostics;
    using System.Globalization;
    using System.Runtime.Caching;

    public class SignaledChangeEventArgs : EventArgs
    {
        public string Name { get; private set; }
        public SignaledChangeEventArgs(string name = null) { this.Name = name; }
    }

    /// <summary>
    /// Cache change monitor that allows an app to fire a change notification
    /// to all associated cache items.
    /// </summary>
    public class SignaledChangeMonitor : ChangeMonitor
    {
        // Shared across all SignaledChangeMonitors in the AppDomain
        private static event EventHandler<SignaledChangeEventArgs> Signaled;

        private string _name;
        private string _uniqueId = Guid.NewGuid().ToString("N", CultureInfo.InvariantCulture);

        public override string UniqueId
        {
            get { return _uniqueId; }
        }

        public SignaledChangeMonitor(string name = null)
        {
            _name = name;
            // Register instance with the shared event
            SignaledChangeMonitor.Signaled += OnSignalRaised;
            base.InitializationComplete();
        }

        public static void Signal(string name = null)
        {
            if (Signaled != null)
            {
                // Raise shared event to notify all subscribers
                Signaled(null, new SignaledChangeEventArgs(name));
            }
        }

        protected override void Dispose(bool disposing)
        {
            SignaledChangeMonitor.Signaled -= OnSignalRaised;
        }

        private void OnSignalRaised(object sender, SignaledChangeEventArgs e)
        {
            if (string.IsNullOrWhiteSpace(e.Name) || string.Compare(e.Name, _name, true) == 0)
            {
                Debug.WriteLine(
                    _uniqueId + " notifying cache of change.", "SignaledChangeMonitor");
                // Cache objects are obligated to remove entry upon change notification.
                base.OnChanged(null);
            }
        }
    }

    public static class CacheTester
    {
        public static void TestCache()
        {
            MemoryCache cache = MemoryCache.Default;

            // Add data to cache
            for (int idx = 0; idx < 50; idx++)
            {
                cache.Add("Key" + idx.ToString(), "Value" + idx.ToString(), GetPolicy(idx));
            }

            // Flush cached items associated with "NamedData" change monitors
            SignaledChangeMonitor.Signal("NamedData");

            // Flush all cached items
            SignaledChangeMonitor.Signal();
        }

        private static CacheItemPolicy GetPolicy(int idx)
        {
            string name = (idx % 2 == 0) ? null : "NamedData";

            CacheItemPolicy cip = new CacheItemPolicy();
            cip.AbsoluteExpiration = System.DateTimeOffset.UtcNow.AddHours(1);
            cip.ChangeMonitors.Add(new SignaledChangeMonitor(name));
            return cip;
        }
    }
}
Thomas F. Abraham
sumber
8
Sepertinya implementasi untuk fungsionalitas Region yang hilang.
Jowen
Sangat bagus. Saya telah mencoba untuk mengimplementasikan sesuatu menggunakan monitor dan guids memorycache yang dirantai tetapi itu mulai menjadi sedikit jelek ketika saya mencoba untuk memperketat fungsinya.
Chao
7
Saya tidak akan merekomendasikan pola ini untuk penggunaan umum. 1. Lambat, bukan karena kesalahan implementasi, tetapi metode buang sangat lambat. 2. Jika Anda mengeluarkan item dari cache dengan kedaluwarsa, Monitor Perubahan masih dipanggil. 3. Mesin saya menelan semua CPU, dan membutuhkan waktu yang sangat lama untuk menghapus 30k item dari cache ketika saya menjalankan tes kinerja. Beberapa kali setelah menunggu 5+ menit saya baru saja menghentikan tes.
Aaron M
1
@PascalMathys Sayangnya, tidak ada solusi yang lebih baik dari ini. Saya akhirnya menggunakannya, terlepas dari kekurangannya, karena ini masih merupakan solusi yang lebih baik daripada menggunakan pencacahan.
Aaron M
9
@AaronM Apakah solusi ini masih lebih baik daripada hanya membuang cache dan membuat yang baru?
RobSiklos
35

Dari http://connect.microsoft.com/VisualStudio/feedback/details/723620/memorycache-class-needs-a-clear-method

Solusinya adalah:

List<string> cacheKeys = MemoryCache.Default.Select(kvp => kvp.Key).ToList();
foreach (string cacheKey in cacheKeys)
{
    MemoryCache.Default.Remove(cacheKey);
}
magritte.dll
sumber
33
Dari dokumentasi : Mengambil enumerator untuk instance MemoryCache adalah operasi pemblokiran dan sumber daya intensif. Oleh karena itu, enumerator sebaiknya tidak digunakan dalam aplikasi produksi.
TrueWill
3
@emberdude Ini persis sama dengan mengambil enumerator - apa yang Anda lakukan dengan implementasi Select()?
RobSiklos
1
Secara pribadi, saya menggunakan ini dalam fungsi unit test [TestInitialize] untuk membersihkan cache memori untuk setiap pengujian unit. Jika tidak, cache tetap ada di seluruh pengujian unit yang memberikan hasil yang tidak diinginkan saat mencoba membandingkan kinerja antara 2 fungsi.
Jacob Morrison
6
@JacobMorrison bisa dibilang, pengujian unit bukanlah "aplikasi produksi" :)
Mels
1
@Mels bisa dibilang, pengujian unit harus ditulis dengan standar yang sama dengan "aplikasi produksi"! :)
Etherman
21
var cacheItems = cache.ToList();

foreach (KeyValuePair<String, Object> a in cacheItems)
{
    cache.Remove(a.Key);
}
Roger Far
sumber
3
Ini memiliki risiko yang sama dengan tanggapan @ Tony; silakan lihat komentar saya di bawah itu.
TrueWill
@TrueWill Siapa atau dulu @Tony?
Alex Angas
2
@AlexAngas - Dia mungkin telah mengubah namanya menjadi magritte. Lihat juga stackoverflow.com/questions/4183270/…
TrueWill
10

Jika kinerja tidak menjadi masalah maka satu baris yang bagus ini akan melakukan trik:

cache.ToList().ForEach(a => cache.Remove(a.Key));
pengguna425678
sumber
3

Anda juga bisa melakukan sesuatu seperti ini:


Dim _Qry = (From n In CacheObject.AsParallel()
           Select n).ToList()
For Each i In _Qry
    CacheObject.Remove(i.Key)
Next
Kevin
sumber
3

Berlari melintasi ini, dan berdasarkan itu, menulis metode paralel yang sedikit lebih efektif dan jelas:

    public void ClearAll()
    {
        var allKeys = _cache.Select(o => o.Key);
        Parallel.ForEach(allKeys, key => _cache.Remove(key));
    }
Pedro G. Dias
sumber
1
Apakah Anda mengujinya untuk melihat apakah lebih cepat (atau lebih lambat)?
Paul George
1

Saya hanya tertarik untuk membersihkan cache dan menemukan ini sebagai opsi, saat menggunakan c # GlobalCachingProvider

                var cache = GlobalCachingProvider.Instance.GetAllItems();
                if (dbOperation.SuccessLoadingAllCacheToDB(cache))
                {
                    cache.Clear();
                }
Brian
sumber
0

versi jawaban magritte yang sedikit lebih baik.

var cacheKeys = MemoryCache.Default.Where(kvp.Value is MyType).Select(kvp => kvp.Key).ToList();
foreach (string cacheKey in cacheKeys)
{
    MemoryCache.Default.Remove(cacheKey);
}
Khachatur
sumber
0

Anda dapat membuang cache MemoryCache.Default dan kemudian menyetel ulang kolom privat tunggal menjadi nol, untuk membuatnya membuat ulang MemoryCache.Default.

       var field = typeof(MemoryCache).GetField("s_defaultCache",
            BindingFlags.Static |
            BindingFlags.NonPublic);
        field.SetValue(null, null);
pintu99
sumber