在 C#/.Net 中缓存

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/1276569/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-06 14:33:47  来源:igfitidea点击:

Caching in C#/.Net

c#.netcaching

提问by Sebastian Müller

I wanted to ask you what is the best approach to implement a cache in C#? Is there a possibility by using given .NET classes or something like that? Perhaps something like a dictionary that will remove some entries, if it gets too large, but where whose entries won't be removed by the garbage collector?

我想问你在 C# 中实现缓存的最佳方法是什么?是否有可能使用给定的 .NET 类或类似的东西?也许像字典这样的东西会删除一些条目,如果它变得太大,但是垃圾收集器不会删除哪些条目?

采纳答案by Bravax

If you're using ASP.NET, you could use the Cacheclass (System.Web.Caching).

如果您使用的是 ASP.NET,则可以使用Cache类 ( System.Web.Caching)。

Here is a good helper class: c-cache-helper-class

这是一个很好的辅助类:c-cache-helper-class

If you mean caching in a windows form app, it depends on what you're trying to do, and where you're trying to cache the data.

如果您的意思是在 Windows 窗体应用程序中缓存,则取决于您要尝试执行的操作以及尝试缓存数据的位置。

We've implemented a cache behind a Webservice for certain methods
(using the System.Web.Cachingobject.).

我们已经为某些方法
(使用System.Web.Caching对象)在 Web 服务后面实现了一个缓存。

However, you might also want to look at the Caching Application Block. (See here)that is part of the Enterprise Library for .NET Framework 2.0.

但是,您可能还想查看缓存应用程序块。(请参阅此处)它是 .NET Framework 2.0 企业库的一部分。

回答by Toad

You could use a Hashtable

你可以使用哈希表

it has very fast lookups, no key collisions and your data will not garbage collected

它具有非常快的查找,没有键冲突,并且您的数据不会被垃圾收集

回答by uriDium

If you are looking to Cache something in ASP.Net then I would look at the Cache class. For example

如果您希望在 ASP.Net 中缓存某些内容,那么我会查看 Cache 类。例如

Hashtable menuTable = new Hashtable(); 
menuTable.add("Home","default.aspx"); 
Cache["menu"] = menuTable; 

Then to retrieve it again

然后再次检索它

Hashtable menuTable = (Hashtable)Cache["menu"];

回答by Muaddib

If you are using .NET 4 or superior, you can use MemoryCacheclass.

如果您使用 .NET 4 或更高版本,则可以使用MemoryCache类。

回答by itadapter DKh

The cache classes supplied with .NET are handy, but have a major problem - they can not store much data (tens of millions+) of objects for a long time without killing your GC. They work great if you cache a few thousand objects, but the moment you move into millions and keep them around until they propagate into GEN2 - the GC pauses would eventually start to be noticeable when you system comes to low memory threshold and GC needs to sweep all gens.

.NET 提供的缓存类很方便,但有一个主要问题——它们无法在不终止 GC 的情况下长时间存储大量数据(数千万+)对象。如果您缓存几千个对象,它们会很好地工作,但是当您移动到数百万个对象并保留它们直到它们传播到 GEN2 的那一刻 - 当您的系统达到低内存阈值并且 GC 需要清除时,GC 暂停最终会开始变得明显所有氏族。

The practicality is this - if you need to store a few hundred thousand instances - use MS cache. Does not matter if your objects are 2-field or 25 field - its about the number of references.

实用性是这样的——如果你需要存储几十万个实例——使用 MS 缓存。无论您的对象是 2 字段还是 25 字段都无关紧要 - 这与引用的数量有关。

On the other hand there are cases when large RAMs, which are common these days, need to be utilized, i.e. 64 GB. For that we have created a 100% managed memory manager and cache that sits on top of it.

另一方面,在某些情况下,需要使用当今很常见的大型 RAM,即 64 GB。为此,我们创建了一个 100% 托管内存管理器和位于其上的缓存。

Our solution can easily store 300,000,000 object in-memory in-process without taxing GC at all - this is because we store data in large (250 mb) byte[] segments.

我们的解决方案可以轻松地在内存中存储 300,000,000 个对象,而无需对 GC 施加任何负担 - 这是因为我们将数据存储在大 (250 mb) byte[] 段中。

Here is the code: NFX Pile(Apache 2.0)

这是代码:NFX Pile(Apache 2.0)

And video: NFX Pile Cache - Youtube

和视频: NFX Pile Cache - Youtube

回答by Meysam

Your question needs more clarification. C# is a language not a framework. You have to specify which framework you want to implement the caching. If we consider that you want to implement it in ASP.NET it is still depends completely on what you want from Cache. You can decide between in-process cache (which will keep the data inside the heap of your application) and out-of-process cache (in this case you can store the data in other memory than the heap like Amazon Elastic cache server). And there is also another decision to make which is between client caching or serve side caching. Usually in solution you have to develop different solution for caching different data. Because base on four factors (accessibility, persistency, size, cost) you have to make decision which solution you need.

你的问题需要更多的澄清。C# 是一种语言而不是框架。您必须指定要实现缓存的框架。如果我们认为您想在 ASP.NET 中实现它,它仍然完全取决于您想要从缓存中获得什么。您可以在进程内缓存(将数据保存在应用程序的堆中)和进程外缓存(在这种情况下,您可以将数据存储在堆以外的其他内存中,如 Amazon Elastic 缓存服务器)之间做出决定。还有另一个决定是在客户端缓存还是服务端缓存之间。通常在解决方案中,您必须开发不同的解决方案来缓存不同的数据。因为基于四个因素(可访问性、持久性、大小、成本),您必须决定您需要哪种解决方案。

回答by alastairtree

MemoryCachein the framework is a good place to start, but you might also like to consider the open source library LazyCachebecause it has a simpler API than memory cache and has built in locking as well as some other developer friendly features. It is also available on nuget.

框架中的MemoryCache是一个很好的起点,但您可能还想考虑开源库LazyCache,因为它具有比内存缓存更简单的 API,并且内置了锁定以及其他一些对开发人员友好的功能。它也可以在 nuget 上找到。

To give you an example:

给你一个例子:

// Create our cache service using the defaults (Dependency injection ready).
// Uses MemoryCache.Default as default so cache is shared between instances
IAppCache cache = new CachingService();

// Declare (but don't execute) a func/delegate whose result we want to cache
Func<ComplexObjects> complexObjectFactory = () => methodThatTakesTimeOrResources();

// Get our ComplexObjects from the cache, or build them in the factory func 
// and cache the results for next time under the given key
ComplexObject cachedResults = cache.GetOrAdd("uniqueKey", complexObjectFactory);

I recently wrote this article about getting started with caching in dot netthat you may find useful.

我最近写了这篇关于dot net 缓存入门的文章,您可能会发现它很有用。

(Disclaimer: I am the author of LazyCache)

(免责声明:我是 LazyCache 的作者)

回答by Mark

I wrote this some time ago and it seems to work well. It allows you to differentiate different cache stores by using different Types: ApplicationCaching<MyCacheType1>, ApplicationCaching<MyCacheType2>....

我前段时间写了这个,它似乎工作得很好。它可以让你通过使用不同的类型来区分不同的缓存存储:ApplicationCaching<MyCacheType1>, ApplicationCaching<MyCacheType2>...

You can decide to allow some stores to persist after execution and others to expire.

您可以决定允许某些存储在执行后保留,而其他存储过期。

You will need a reference to the Newtonsoft.Jsonserializer (or use an alternative one) and of course all objects or values types to be cached must be serializable.

您将需要对Newtonsoft.Json序列化程序的引用(或使用替代程序),当然所有要缓存的对象或值类型都必须是可序列化的。

Use MaxItemCountto set a limit to the number of items in any one store.

用于MaxItemCount对任何一家商店中的商品数量设置限制。

A separate Zipper class (see code below) uses System.IO.Compression. This minimises the size of the store and helps speed up loading times.

一个单独的 Zipper 类(见下面的代码)使用System.IO.Compression. 这最大限度地减少了商店的大小并有助于加快加载时间。


public static class ApplicationCaching<K> 
{
        //====================================================================================================================
        public static event EventHandler InitialAccess = (s, e) => { };
        //=============================================================================================
        static Dictionary<string, byte[]> _StoredValues;
        static Dictionary<string, DateTime> _ExpirationTimes = new Dictionary<string, DateTime>();
        //=============================================================================================
        public static int MaxItemCount { get; set; } = 0;
        private static void OnInitialAccess()
        {
            //-----------------------------------------------------------------------------------------
            _StoredValues = new Dictionary<string, byte[]>();
            //-----------------------------------------------------------------------------------------
            InitialAccess?.Invoke(null, EventArgs.Empty);
            //-----------------------------------------------------------------------------------------
        }
        public static void AddToCache<T>(string key, T value, DateTime expirationTime)
        {
            try
            {
                //-----------------------------------------------------------------------------------------
                if (_StoredValues is null) OnInitialAccess();
                //-----------------------------------------------------------------------------------------
                string strValue = JsonConvert.SerializeObject(value);
                byte[] zippedValue = Zipper.Zip(strValue);
                //-----------------------------------------------------------------------------------------
                _StoredValues.Remove(key);
                _StoredValues.Add(key, zippedValue);
                //-----------------------------------------------------------------------------------------
                _ExpirationTimes.Remove(key);
                _ExpirationTimes.Add(key, expirationTime);
                //-----------------------------------------------------------------------------------------
            }
            catch (Exception ex)
            {

                throw ex;
            }
        }
        //=============================================================================================
        public static T GetFromCache<T>(string key, T defaultValue = default)
        {
            try
            {
                //-----------------------------------------------------------------------------------------
                if (_StoredValues is null) OnInitialAccess();
                //-----------------------------------------------------------------------------------------
                if (_StoredValues.ContainsKey(key))
                {
                    //------------------------------------------------------------------------------------------
                    if (_ExpirationTimes[key] <= DateTime.Now)
                    {
                        //------------------------------------------------------------------------------------------
                        _StoredValues.Remove(key);
                        _ExpirationTimes.Remove(key);
                        //------------------------------------------------------------------------------------------
                        return defaultValue;
                        //------------------------------------------------------------------------------------------
                    }
                    //------------------------------------------------------------------------------------------
                    byte[] zippedValue = _StoredValues[key];
                    //------------------------------------------------------------------------------------------
                    string strValue = Zipper.Unzip(zippedValue);
                    T value = JsonConvert.DeserializeObject<T>(strValue);
                    //------------------------------------------------------------------------------------------
                    return value;
                    //------------------------------------------------------------------------------------------
                }
                else
                {
                    return defaultValue;
                }
                //---------------------------------------------------------------------------------------------
            }
            catch (Exception ex)
            {
                throw ex;
            }
        }
        //=============================================================================================
        public static string ConvertCacheToString()
        {
            //-----------------------------------------------------------------------------------------
            if (_StoredValues is null || _ExpirationTimes is null) return "";
            //-----------------------------------------------------------------------------------------
            List<string> storage = new List<string>();
            //-----------------------------------------------------------------------------------------
            string strStoredObject = JsonConvert.SerializeObject(_StoredValues);
            string strExpirationTimes = JsonConvert.SerializeObject(_ExpirationTimes);
            //-----------------------------------------------------------------------------------------
            storage.AddRange(new string[] { strStoredObject, strExpirationTimes});
            //-----------------------------------------------------------------------------------------
            string strStorage = JsonConvert.SerializeObject(storage);
            //-----------------------------------------------------------------------------------------
            return strStorage;
            //-----------------------------------------------------------------------------------------
        }
        //=============================================================================================
        public static void InializeCacheFromString(string strCache)
        {
            try
            {
                //-----------------------------------------------------------------------------------------
                List<string> storage = JsonConvert.DeserializeObject<List<string>>(strCache);
                //-----------------------------------------------------------------------------------------
                if (storage != null && storage.Count == 2)
                {
                    //-----------------------------------------------------------------------------------------
                    _StoredValues = JsonConvert.DeserializeObject<Dictionary<string, byte[]>>(storage.First());
                    _ExpirationTimes = JsonConvert.DeserializeObject<Dictionary<string, DateTime>>(storage.Last());
                    //-----------------------------------------------------------------------------------------
                    if (_ExpirationTimes != null && _StoredValues != null)
                    {
                        //-----------------------------------------------------------------------------------------
                        for (int i = 0; i < _ExpirationTimes.Count; i++)
                        {
                            string key = _ExpirationTimes.ElementAt(i).Key;
                            //-----------------------------------------------------------------------------------------
                            if (_ExpirationTimes[key] < DateTime.Now)
                            {
                                ClearItem(key);
                            }
                            //-----------------------------------------------------------------------------------------
                        }

                        //-----------------------------------------------------------------------------------------
                        if (MaxItemCount > 0 && _StoredValues.Count > MaxItemCount)
                        {
                            IEnumerable<KeyValuePair<string, DateTime>> countedOutItems = _ExpirationTimes.OrderByDescending(o => o.Value).Skip(MaxItemCount);
                            for (int i = 0; i < countedOutItems.Count(); i++)
                            {
                                ClearItem(countedOutItems.ElementAt(i).Key);
                            }
                        }
                        //-----------------------------------------------------------------------------------------
                        return;
                        //-----------------------------------------------------------------------------------------
                    }
                    //-----------------------------------------------------------------------------------------
                }
                //-----------------------------------------------------------------------------------------
                _StoredValues = new Dictionary<string, byte[]>();
                _ExpirationTimes = new Dictionary<string, DateTime>();
                //-----------------------------------------------------------------------------------------
            }
            catch (Exception)
            {
                throw;
            }
        }
        //=============================================================================================
        public static void ClearItem(string key)
        {
            //-----------------------------------------------------------------------------------------
            if (_StoredValues.ContainsKey(key))
            {
                _StoredValues.Remove(key);
            }
            //-----------------------------------------------------------------------------------------
            if (_ExpirationTimes.ContainsKey(key))
                _ExpirationTimes.Remove(key);
            //-----------------------------------------------------------------------------------------
        }
        //=============================================================================================
    }

You can easily start using the cache on the fly with something like...

您可以轻松地开始使用缓存,例如...

            //------------------------------------------------------------------------------------------------------------------------------
            string key = "MyUniqueKeyForThisItem";
            //------------------------------------------------------------------------------------------------------------------------------
            MyType obj = ApplicationCaching<MyCacheType>.GetFromCache<MyType>(key);
            //------------------------------------------------------------------------------------------------------------------------------

            if (obj == default)
            {
                obj = new MyType(...);
                ApplicationCaching<MyCacheType>.AddToCache(key, obj, DateTime.Now.AddHours(1));
            }

Note the actual types stored in the cache can be the same or different from the cache type. The cache type is ONLY used to differentiate different cache stores.

请注意,缓存中存储的实际类型可以与缓存类型相同或不同。缓存类型仅用于区分不同的缓存存储。

You can then decide to allow the cache to persist after execution terminates using Default Settings

然后,您可以决定允许缓存在执行终止后持续使用 Default Settings

string bulkCache = ApplicationCaching<MyType>.ConvertCacheToString();
                //--------------------------------------------------------------------------------------------------------
                if (bulkCache != "")
                {
                    Properties.Settings.Default.*MyType*DataCachingStore = bulkCache;
                }
                //--------------------------------------------------------------------------------------------------------
                try
                {
                    Properties.Settings.Default.Save();
                }
                catch (IsolatedStorageException)
                {
                    //handle Isolated Storage exceptions here
                }

Handle the InitialAccess Event to reinitialize the cache when you restart the app

重新启动应用程序时处理 InitialAccess 事件以重新初始化缓存

private static void ApplicationCaching_InitialAccess(object sender, EventArgs e)
        {
            //-----------------------------------------------------------------------------------------
            string storedCache = Properties.Settings.Default.*MyType*DataCachingStore;
            ApplicationCaching<MyCacheType>.InializeCacheFromString(storedCache);
            //-----------------------------------------------------------------------------------------
        }

Finally here is the Zipper class...

最后是拉链类......

public class Zipper
    {
        public static void CopyTo(Stream src, Stream dest)
        {
            byte[] bytes = new byte[4096];

            int cnt;

            while ((cnt = src.Read(bytes, 0, bytes.Length)) != 0)
            {
                dest.Write(bytes, 0, cnt);
            }
        }

        public static byte[] Zip(string str)
        {
            var bytes = Encoding.UTF8.GetBytes(str);

            using (var msi = new MemoryStream(bytes))
            using (var mso = new MemoryStream())
            {
                using (var gs = new GZipStream(mso, CompressionMode.Compress))
                {
                    CopyTo(msi, gs);
                }
                return mso.ToArray();
            }
        }

        public static string Unzip(byte[] bytes)
        {
            using (var msi = new MemoryStream(bytes))
            using (var mso = new MemoryStream())
            {
                using (var gs = new GZipStream(msi, CompressionMode.Decompress))
                {
                    CopyTo(gs, mso);
                }
                return Encoding.UTF8.GetString(mso.ToArray());
            }
        }
    }