C# MemoryCache学习笔记
很多情况下需要用到缓存,合理利用缓存一方面可以提高程序的响应速度,同时可以减少对特定资源访问的压力。为了避免每次请求都去访问后台的
资源(例如数据库),一般会考虑将一些更新不是很频繁的、可以重用的数据,通过一定的方式临时地保存起来,后续的请求根据情况可以直接访问这
些保存起来的数据,这种机制就是所谓的缓存机制。
.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,并添加一个按钮。
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、运行结果如下:
参考自: