欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

C# MemoryCache学习笔记

程序员文章站 2023-01-26 16:38:32
很多情况下需要用到缓存,合理利用缓存一方面可以提高程序的响应速度,同时可以减少对特定资源访问的压力。为了避免每次请求都去访问后台的 资源(例如数据库),一般会考虑将一些更新不是很频繁的、可以重用的数据,通过一定的方式临时地保存起来,后续的请求根据情况可以直接访问这 些保存起来的数据,这种机制就是所谓 ......

    很多情况下需要用到缓存,合理利用缓存一方面可以提高程序的响应速度,同时可以减少对特定资源访问的压力。为了避免每次请求都去访问后台的

资源(例如数据库),一般会考虑将一些更新不是很频繁的、可以重用的数据,通过一定的方式临时地保存起来,后续的请求根据情况可以直接访问这

些保存起来的数据,这种机制就是所谓的缓存机制。

    .net 4.0的缓存功能主要由三部分组成:system.runtime.caching,system.web.caching.cache和output cache。

    memorycache:这个是在.net 4.0中新增的缓存框架,namespace:system.runtime.caching ,assembly:system.runtime.caching.dll。

    system.web.caching.cache:这个是在.net 2.0开始就一直存在的缓存对象,一般主要用在web中,当然也可以用于winform里面,不过要引用

system.web.dll。

    output cache:这个是在asp.net里面使用的。在asp.net 4.0之前,都是直接使用system.web.caching.cache来缓存html片段。在asp.net 4.0

中对它进行了重新设计,提供了一个outputcacheprovider供开发人员进行扩展,但是它默认情况下,仍然使用system.web.caching.cache来做做缓存。

    下面演示memorycache的简单使用:

    1、添加一个类,命名为confighelper,代码如下:

    /// <summary>
    /// 配置帮助类
    /// </summary>
    class confighelper
    {
        /// <summary>
        /// 获取管理配置文件对象
        /// </summary>
        /// <param name="configpath">指定要管理的配置文件路径,如果为空或不存在,则为管理程序集默认的配置文件路径。</param>
        /// <returns></returns>
        private static configuration getconfiguration(string configpath = null)
        {
            if (!string.isnullorempty(configpath) && file.exists(configpath))
            {
                execonfigurationfilemap map = new execonfigurationfilemap
                {
                    execonfigfilename = configpath
                };
                return configurationmanager.openmappedexeconfiguration(map, configurationuserlevel.none);
            }
            else
            {
                return configurationmanager.openexeconfiguration(configurationuserlevel.none);
            }
        }

        /// <summary>
        /// 获取指定配置文件+配置名称的配置项的值
        /// </summary>
        public static string getappsettingvalue(string key, string defaultvalue = null, string configpath = null)
        {
            var config = getconfiguration(configpath);
            var appsetting = config.appsettings.settings[key];
            return appsetting.value;
        }

        /// <summary>
        /// 获取所有配置值
        /// </summary>
        public static dictionary<string, string> getappsettingvalues(string configpath = null)
        {
            dictionary<string, string> settingdic = new dictionary<string, string>();
            var config = getconfiguration(configpath);
            var settings = config.appsettings.settings;
            foreach (string key in settings.allkeys)
            {
                settingdic[key] = settings[key].tostring();
            }
            return settingdic;
        }

        /// <summary>
        /// 设置配置值(存在则更新,不存在则新增。)
        /// </summary>
        public static void setappsettingvalue(string key, string value, string configpath = null)
        {
            var config = getconfiguration(configpath);
            var setting = config.appsettings.settings[key];
            if (setting == null)
            {
                config.appsettings.settings.add(key, value);
            }
            else
            {
                setting.value = value;
            }

            config.save(configurationsavemode.modified);
            configurationmanager.refreshsection("appsettings");
        }

        /// <summary>
        /// 设置多个配置值(存在则更新,不存在则新增)
        /// </summary>
        public static void setappsettingvalues(ienumerable<keyvaluepair<string, string>> settingvalues, string configpath = null)
        {
            var config = getconfiguration(configpath);
            foreach (var item in settingvalues)
            {
                var setting = config.appsettings.settings[item.key];
                if (setting == null)
                {
                    config.appsettings.settings.add(item.key, item.value);
                }
                else
                {
                    setting.value = item.value;
                }
            }
            config.save(configurationsavemode.modified);
            configurationmanager.refreshsection("appsettings");
        }

        /// <summary>
        /// 删除配置值
        /// </summary>
        public static void removeappsetting(string key, string configpath = null)
        {
            var config = getconfiguration(configpath);
            config.appsettings.settings.remove(key);
            config.save(configurationsavemode.modified);
            configurationmanager.refreshsection("appsettings");
        }

        /// <summary>
        /// 删除多个配置值
        /// </summary>
        public static void removeappsettings(string configpath = null, params string[] keys)
        {
            var config = getconfiguration(configpath);
            if (keys != null)
            {
                foreach (string key in keys)
                {
                    config.appsettings.settings.remove(key);
                }
            }
            else
            {
                config.appsettings.settings.clear();
            }
            config.save(configurationsavemode.modified);
            configurationmanager.refreshsection("appsettings");
        }

        /// <summary>
        /// 获取连接字符串
        /// </summary>
        public static string getconnectionstring(string name, string defaultconnstring = null, string configpath = null)
        {
            var config = getconfiguration(configpath);
            var connstringsettings = config.connectionstrings.connectionstrings[name];
            if (connstringsettings == null)
            {
                return defaultconnstring;
            }
            return connstringsettings.connectionstring;
        }

        /// <summary>
        /// 获取指定配置文件+连接名称的连接字符串配置项
        /// </summary>
        public static connectionstringsettings getconnectionstringsetting(string name, string configpath = null)
        {
            var config = getconfiguration(configpath);
            var connstringsettings = config.connectionstrings.connectionstrings[name];
            return connstringsettings;
        }

        /// <summary>
        /// 获取所有的连接字符串配置项
        /// </summary>
        public static dictionary<string, connectionstringsettings> getconnectionstringsettings(string configpath = null)
        {
            var config = getconfiguration(configpath);
            var connstringsettingdic = new dictionary<string, connectionstringsettings>();
            var connstringsettings = configurationmanager.connectionstrings;
            foreach (connectionstringsettings item in connstringsettings)
            {
                connstringsettingdic[item.name] = item;
            }
            return connstringsettingdic;
        }

        /// <summary>
        /// 设置连接字符串的值(存在则更新,不存在则新增。)
        /// </summary>
        public static void setconnectionstring(string name, string connstring, string provider, string configpath = null)
        {
            var config = getconfiguration(configpath);
            connectionstringsettings connstringsettings = config.connectionstrings.connectionstrings[name];
            if (connstringsettings != null)
            {
                connstringsettings.connectionstring = connstring;
                connstringsettings.providername = provider;
            }
            else
            {
                connstringsettings = new connectionstringsettings(name, connstring, provider);
                config.connectionstrings.connectionstrings.add(connstringsettings);
            }

            config.save(configurationsavemode.modified);
            configurationmanager.refreshsection("connectionstrings");
        }

        /// <summary>
        /// 设置多个连接字符串的值(存在则更新,不存在则新增。)
        /// </summary>
        public static void setconnectionstrings(ienumerable<connectionstringsettings> connstringsettings, string configpath = null)
        {
            var config = getconfiguration(configpath);
            foreach (var item in connstringsettings)
            {
                connectionstringsettings connstringsetting = config.connectionstrings.connectionstrings[item.name];
                if (connstringsetting != null)
                {
                    connstringsetting.connectionstring = item.connectionstring;
                    connstringsetting.providername = item.providername;
                }
                else
                {
                    config.connectionstrings.connectionstrings.add(item);
                }
            }

            config.save(configurationsavemode.modified);
            configurationmanager.refreshsection("connectionstrings");
        }

        /// <summary>
        /// 删除连接字符串配置项
        /// </summary>
        public static void removeconnectionstring(string name, string configpath = null)
        {
            var config = getconfiguration(configpath);
            config.connectionstrings.connectionstrings.remove(name);
            config.save(configurationsavemode.modified);
            configurationmanager.refreshsection("connectionstrings");
        }

        /// <summary>
        /// 删除多个连接字符串配置项
        /// </summary>
        public static void removeconnectionstrings(string configpath = null, params string[] names)
        {
            var config = getconfiguration(configpath);
            if (names != null)
            {
                foreach (string name in names)
                {
                    config.connectionstrings.connectionstrings.remove(name);
                }
            }
            else
            {
                config.connectionstrings.connectionstrings.clear();
            }
            config.save(configurationsavemode.modified);
            configurationmanager.refreshsection("connectionstrings");
        }
    }

    2、添加一个类,命名为memorycachehelper(注意需引用system.runtime.caching.dll),代码如下:

    /// <summary>
    /// 内存缓存帮助类,支持绝对过期时间、滑动过期时间、文件依赖三种缓存方式。
    /// </summary>
    class memorycachehelper
    {
        private static readonly object _locker1 = new object(), _locker2 = new object();

        /// <summary>
        /// 取缓存项,如果不存在则返回空。
        /// </summary>
        /// <typeparam name="t"></typeparam>
        /// <param name="key"></param>
        /// <returns></returns>
        public static t getcacheitem<t>(string key)
        {
            try
            {
                return (t)memorycache.default[key];
            }
            catch
            {
                return default(t);
            }
        }

        /// <summary>
        /// 是否包含指定键的缓存项
        /// </summary>
        /// <param name="key"></param>
        /// <returns></returns>
        public static bool contains(string key)
        {
            return memorycache.default.contains(key);
        }

        /// <summary>
        /// 取缓存项,如果不存在则新增缓存项。
        /// </summary>
        /// <typeparam name="t"></typeparam>
        /// <param name="key"></param>
        /// <param name="cachepopulate"></param>
        /// <param name="slidingexpiration"></param>
        /// <param name="absoluteexpiration"></param>
        /// <returns></returns>
        public static t getoraddcacheitem<t>(string key, func<t> cachepopulate, timespan? slidingexpiration = null, datetime? absoluteexpiration = null)
        {
            if (string.isnullorwhitespace(key)) throw new argumentexception("invalid cache key");
            if (cachepopulate == null) throw new argumentnullexception("cachepopulate");
            if (slidingexpiration == null && absoluteexpiration == null) throw new argumentexception("either a sliding expiration or absolute must be provided");

            if (memorycache.default[key] == null)
            {
                lock (_locker1)
                {
                    if (memorycache.default[key] == null)
                    {
                        t cachevalue = cachepopulate();
                        if (!typeof(t).isvaluetype && cachevalue == null)   //如果是引用类型且为null则不存缓存
                        {
                            return cachevalue;
                        }

                        var item = new cacheitem(key, cachevalue);
                        var policy = createpolicy(slidingexpiration, absoluteexpiration);

                        memorycache.default.add(item, policy);
                    }
                }
            }

            return (t)memorycache.default[key];
        }

        /// <summary>
        /// 取缓存项,如果不存在则新增缓存项。
        /// </summary>
        /// <typeparam name="t"></typeparam>
        /// <param name="key"></param>
        /// <param name="cachepopulate"></param>
        /// <param name="dependencyfilepath"></param>
        /// <returns></returns>
        public static t getoraddcacheitem<t>(string key, func<t> cachepopulate, string dependencyfilepath)
        {
            if (string.isnullorwhitespace(key)) throw new argumentexception("invalid cache key");
            if (cachepopulate == null) throw new argumentnullexception("cachepopulate");

            if (memorycache.default[key] == null)
            {
                lock (_locker2)
                {
                    if (memorycache.default[key] == null)
                    {
                        t cachevalue = cachepopulate();
                        if (!typeof(t).isvaluetype && cachevalue == null)   //如果是引用类型且为null则不存缓存
                        {
                            return cachevalue;
                        }

                        var item = new cacheitem(key, cachevalue);
                        var policy = createpolicy(dependencyfilepath);

                        memorycache.default.add(item, policy);
                    }
                }
            }

            return (t)memorycache.default[key];
        }

        /// <summary>
        /// 指定缓存项的一组逐出和过期详细信息
        /// </summary>
        /// <param name="slidingexpiration"></param>
        /// <param name="absoluteexpiration"></param>
        /// <returns></returns>
        private static cacheitempolicy createpolicy(timespan? slidingexpiration, datetime? absoluteexpiration)
        {
            var policy = new cacheitempolicy();

            if (absoluteexpiration.hasvalue)
            {
                policy.absoluteexpiration = absoluteexpiration.value;
            }
            else if (slidingexpiration.hasvalue)
            {
                policy.slidingexpiration = slidingexpiration.value;
            }

            policy.priority = cacheitempriority.default;

            return policy;
        }

        /// <summary>
        /// 指定缓存项的一组逐出和过期详细信息
        /// </summary>
        /// <param name="filepath"></param>
        /// <returns></returns>
        private static cacheitempolicy createpolicy(string filepath)
        {
            cacheitempolicy policy = new cacheitempolicy();
            policy.changemonitors.add(new hostfilechangemonitor(new list<string>() { filepath }));
            policy.priority = cacheitempriority.default;
            return policy;
        }

        /// <summary>
        /// 移除指定键的缓存项
        /// </summary>
        /// <param name="key"></param>
        public static void removecacheitem(string key)
        {
            if (contains(key))
            {
                memorycache.default.remove(key);
            }
        }
    }

    3、添加一个winform窗体,命名为main,并添加一个按钮。

C# MemoryCache学习笔记

    4、配置app.config:

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <startup> 
    <supportedruntime version="v4.0" sku=".netframework,version=v4.6.1" />
  </startup>
  <connectionstrings>
    <add name ="connstring" connectionstring="server=.;database=db_test;uid=sa;pwd=********;" />
  </connectionstrings>
</configuration>

    5、main窗体代码如下:

        private void button1_click(object sender, eventargs e)
        {
            int times1 = 0, times2 = 0;
            for (int i = 0; i < 10; i++)
            {
                if (memorycachehelper.contains("connstring"))
                {
                    times1++;
                }
                else
                {
                    times2++;
                    string connstr = memorycachehelper.getoraddcacheitem("connstring", () =>
                    {
                        return confighelper.getconnectionstring("connstring", null);
                    }, application.startuppath + @"\app.config");
                }
            }
            messagebox.show($"内存缓存读取次数:{times1},非内存缓存读取次数:{times2}", "提示", messageboxbuttons.ok, messageboxicon.information);
        }

    6、运行结果如下:

C# MemoryCache学习笔记

    参考自: