.Net缓存小结(上)
缓存技术 被认为是减轻服务器负载、降低网络拥塞、增强Web可扩展性的有效途径之一,其基本思想是利用客户访问的时间局部性(Temporal Locality)原理,将客户访问过的内容在Cache中存放一个副本,当该内容下次被访问时,不必连接到驻留网站,而是由Cache中保留的副本提供。通常,应用程序可以将那些频繁访问的数据,以及那些需要大量处理时间来创建的数据存储在内存中,从而提高性能。同样,如果应用程序包含一个处理复杂数据但不需要经常更新的页,也可以缓存起来 使得每次请求时服务器就不用再重新创建该页提高工作效率。
大家可以去参考一下 使用缓存的9大误区 了解一下缓存的优缺点及注意事项。
.Net传统缓存方式:
Application 存放在服务器端,作用于整个应用程序的生命周期,可以存储任意大小的数据,所有用户都可以使用。
Session 存放在服务器端,作用于某个用户活动时间 + 延迟时间(默认20分钟),存储小量数据,只能单个用户使用。
Cookie 存放在客户端,作用于程序指定生命周期,存储小量数据,只能单个用户使用。
.Net控件缓存方式:
ViewState 存放在客户端,作用于一个web页面的生命期,存储小量数据,只能单个用户使用。
下面分别简单介绍一下这几种缓存的使用方法,贴一下测试代码:
Application
Application:用于保存所有用户共用的数据信息。 在Asp.Net中类似的配置数据最好保存在Web.config文件中。如果使用Application对象,一个需要考虑的问题是任何写操作都要在 Application_OnStart事件(Global.asax)中完成。尽管使用Application.Lock和Application.Unlock方法来避免写操作的同步,但是它串行化了Application对象的请求,当网站访问量大的时候会产生严重的性能。因此最好不要用此对象保存大的数据集。
Application对象是HttpApplicationState类的实例。来看下Application提供的功能:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
//启用 ASP.NET 应用程序中多个会话和请求之间的全局信息共享。 public sealed class HttpApplicationState : NameObjectCollectionBase { // 获取 System.Web.HttpApplicationState 集合中的访问键。 // 返回结果: System.Web.HttpApplicationState 对象名的字符串数组。 public string[] AllKeys { get; } // 获取对 System.Web.HttpApplicationState 对象的引用。 // 返回结果: 对 System.Web.HttpApplicationState 对象的引用。 public HttpApplicationState Contents { get; } // 获取 System.Web.HttpApplicationState 集合中的对象数。 // 返回结果: 集合中的 Item 对象数。 默认值为 0。 public override int Count { get; } // 获取由 <object> 标记声明的所有对象,其中范围设置为 ASP.NET 应用程序中的“Application”。 // 返回结果: 页上的对象集合。 public HttpStaticObjectsCollection StaticObjects { get; } // 通过索引获取单个 System.Web.HttpApplicationState 对象。 // 参数: index: 集合中对象的数字索引。 // 返回结果: index 所引用的对象。 public object this[int index] { get; } // 通过名称获取单个 System.Web.HttpApplicationState 对象的值。 // 参数: name: 集合中的对象名。 // 返回结果: name 所引用的对象。 public object this[string name] { get; set; } // 将新的对象添加到 System.Web.HttpApplicationState 集合中。 // 参数: name: 要添加到集合中的对象名。value:对象的值。 public void Add(string name, object value); // 从 System.Web.HttpApplicationState 集合中移除所有对象。 public void Clear(); // 通过数字索引获取 System.Web.HttpApplicationState 对象。 // 参数: index: 应用程序状态对象的索引。 // 返回结果: index 所引用的对象。 public object Get(int index); // 通过名称获取 System.Web.HttpApplicationState 对象。 // 参数: name: 对象的名称。 // 返回结果: name 所引用的对象。 public object Get(string name); // 通过索引获取 System.Web.HttpApplicationState 对象名。 // 参数: index: 应用程序状态对象的索引。 // 返回结果: 保存应用程序状态对象所使用的名称。 public string GetKey(int index); // 锁定对 System.Web.HttpApplicationState 变量的访问以促进访问同步。 public void Lock(); // 从 System.Web.HttpApplicationState 集合中移除命名对象。 // 参数: name: 要从集合中移除的对象名。 public void Remove(string name); // 从 System.Web.HttpApplicationState 集合中移除所有对象。 public void RemoveAll(); // 按索引从集合中移除一个 System.Web.HttpApplicationState 对象。 // 参数: index: 要移除的项在集合中的位置。 public void RemoveAt(int index); // 更新 System.Web.HttpApplicationState 集合中的对象值。 // 参数: name: 要更新的对象名。 value: 对象更新之后的值。 public void Set(string name, object value); // 取消锁定对 System.Web.HttpApplicationState 变量的访问以促进访问同步。 public void UnLock(); }
贴做一些测试代码
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
//在global.asax.cs 中添加一些Application对象 protected void Application_Start() { AreaRegistration.RegisterAllAreas(); WebApiConfig.Register(GlobalConfiguration.Configuration); FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters); RouteConfig.RegisterRoutes(RouteTable.Routes); Application.Add("一", "我是第一个"); Application.Add("二", "我是第二个"); Application["三"] = "我是第三个"; Application["四"] = "我是第四个"; } /// <summary> /// Application 缓存测试方法 /// </summary> public static void ApplicationTest() { HttpApplicationState application = HttpContext.Current.Application; string[] strArr = application.AllKeys;//获取所有的key组成的字符串数组 strArr = { "一","二","三","四" } //获取全局对象的引用,这样就可以在其他页面处理全局对象,不会限制在Global.asax中。 HttpApplicationState appContents = application.Contents; //获取由 <object> 标记声明的所有对象,其中范围设置为 ASP.NET 应用程序中的“Application” var staObj = application.StaticObjects; int count = application.Count;//获取所有对象总数 输出:4 var strValue = application["一"];//可以这样按照 key 获取值 输出:我是1 var strIndex = application[1];//可以这样按照索引号获取值//索引号从0开始 输出:我是2 application.Lock();//锁定变量 防止同时操作 appContents.Add("五", "我是5");//添加一个对象 string strValueByKey = application.Get("一").ToString();//按照 key 获取值 输出:我是1 string strValueByIndex = application.Get(1).ToString();//按照索引号获取值 输出:我是2 string strKey = application.GetKey(1);//按照索引号,获取 key 名称 输出:我是2 application.Set("一", "我是修改后的1");//修改值 application.Remove("二");//按照 key 移除一个对象 application.RemoveAt(2);//按照索引号移除一个对象 application.RemoveAll();//移除所有对象 application.Clear();//移除所有对象 application.UnLock();//操作完成 解除锁定 }
Session
Session:用于保存每个用户的专用信息。Session中的信息保存在Web服务器的内存中,保存的数据量可大可小。当Session超时或被关闭时将自动释放保存的数据信息。对于小量的数据Session对象保存还是一个不错的选择。Session的生存期是用户持续请求时间加上一段时间(一般是20分钟左右)由于用户停止使用应用程序之后它仍在内存中存留一段时间,因此这种方法效率较低。
看一下HttpSessionState提供的功能:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
// 提供对会话状态值以及会话级别设置和生存期管理方法的访问。 public sealed class HttpSessionState : ICollection, IEnumerable { // 获取或设置当前会话的字符集标识符。 // 返回结果: 当前会话的字符集标识符。 public int CodePage { get; set; } // 摘要: 获取对当前会话状态对象的引用。 // 返回结果: 当前 System.Web.SessionState.HttpSessionState。 public HttpSessionState Contents { get; } // 获取一个值,该值指示是否为无 Cookie 会话配置应用程序。 // 返回结果: 指示是否为无 Cookie 会话配置应用程序的其中一个 System.Web.HttpCookieMode 值。 默认值为 System.Web.HttpCookieMode.UseCookies。 public HttpCookieMode CookieMode { get; } // 获取会话状态集合中的项数。 // 返回结果: 集合中的项数。 public int Count { get; } // 获取一个值,该值指示会话 ID 是嵌入在 URL 中还是存储在 HTTP Cookie 中。 // 返回结果: 如果会话嵌入在 URL 中,则为 true;否则,为 false。 public bool IsCookieless { get; } // 获取一个值,该值指示会话是否是与当前请求一起创建的。 // 返回结果: 如果会话是与当前请求一起创建的,则为 true;否则,为 false。 public bool IsNewSession { get; } // 获取一个值,该值指示会话是否为只读。 // 返回结果: 如果会话为只读,则为 true;否则为 false。 public bool IsReadOnly { get; } // 获取一个值,该值指示对会话状态值的集合的访问是否是同步(线程安全)的。 // 返回结果: 如果对该集合的访问是同步的(线程安全),则为 true;否则,为 false。 public bool IsSynchronized { get; } // 获取存储在会话状态集合中所有值的键的集合。 // 返回结果: 包含所有会话键的 System.Collections.Specialized.NameObjectCollectionBase.KeysCollection。 public NameObjectCollectionBase.KeysCollection Keys { get; } // 获取或设置当前会话的区域设置标识符 (LCID)。 // 返回结果: 指定当前会话的区域性的 System.Globalization.CultureInfo 实例。 public int LCID { get; set; } // 获取当前会话状态模式。 // 返回结果: System.Web.SessionState.SessionStateMode 值之一。 public SessionStateMode Mode { get; } // 获取会话的唯一标识符。 // 返回结果: 唯一会话标识符。 public string SessionID { get; } // 获取由 ASP.NET 应用程序文件 Global.asax 中的 <object Runat="Server" Scope="Session"/> 标记声明的对象的集合。 // 返回结果: 一个 System.Web.HttpStaticObjectsCollection,它包含在 Global.asax 文件中声明的对象。 public HttpStaticObjectsCollection StaticObjects { get; } // 获取一个对象,该对象可用于同步对会话状态值的集合的访问。 // 返回结果: 可用于同步集合访问的对象。 public object SyncRoot { get; } // 获取并设置在会话状态提供程序终止会话之前各请求之间所允许的时间(以分钟为单位)。 // 返回结果: 超时期限(以分钟为单位)。 public int Timeout { get; set; } //按数字索引获取或设置会话值。 // 参数: index: 会话值的数字索引。 // 返回结果: 存储在指定索引处的会话状态值;如果该项不存在,则为 null。 public object this[int index] { get; set; } // 按名称获取或设置会话值。 // 参数: name: 会话值的键名。 // 返回结果: 具有指定名称的会话状态值;如果该项不存在,则为 null。 public object this[string name] { get; set; } //取消当前会话。 public void Abandon(); // 向会话状态集合添加一个新项。 // 参数: name: 要添加到会话状态集合的项的名称。 value: 要添加到会话状态集合的项的值。 public void Add(string name, object value); // 从会话状态集合中移除所有的键和值。 public void Clear(); // 将会话状态值的集合复制到一维数组中(从数组的指定索引处开始)。 // 参数: array: System.Array,它接收会话值。 index: array 中从零开始的索引,在此处开始复制。 public void CopyTo(Array array, int index); // 返回一个枚举数,可用来读取当前会话中所有会话状态的变量名称。 // 返回结果: System.Collections.IEnumerator 可以循环访问会话状态集合中的变量名称。 public IEnumerator GetEnumerator(); // 删除会话状态集合中的项。 // 参数: name: 要从会话状态集合中删除的项的名称。 public void Remove(string name); // 从会话状态集合中移除所有的键和值。 public void RemoveAll(); // 删除会话状态集合中指定索引处的项。 // 参数: index: 要从会话状态集合中移除的项的索引。 // 异常: System.ArgumentOutOfRangeException: index 小于零。 - 或 - index 等于或大于 System.Web.SessionState.HttpSessionState.Count。 public void RemoveAt(int index); }
测试代码
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
public static void SessionTest() { HttpSessionState session = HttpContext.Current.Session; session.Add("one", "我是第一个Session"); session.Add("two", "我是第二个Session"); var count = session.Count;//获取session集合项数 输出2 var isCookieless = session.IsCookieless;//输出 false 表示是嵌入在cookie里 var codePage = session.CodePage;//获取当前会话的字符集标识符 var se1 = session.Contents["one"];//se1 ="我是第一个Session" var cookieMode = session.CookieMode;//是否是无cookie会话配置应用程序 输出:UserCookies var isNewSession = session.IsNewSession;//会话是否与当前请求一起新建的 输出:true var isReadOnly = session.IsReadOnly;//是否为只读 输出:false var isSynchronized = session.IsSynchronized;//false var seList = new List<string>(); foreach (string str in session.Keys)//获取Key集合 { seList.Add(session[str].ToString());//遍历获取session对象 } var lcid = session.LCID;//获取当前会话的区域设置标识符 (LCID) var mode = session.Mode;//获取当前会话模式 输出:InProc var sessionId = session.SessionID;//获取会话的唯一标识符 var syncRoot = session.SyncRoot;//获取一个对象用于同步对会话状态值的集合的访问 var timeout = session.Timeout;//默认20 string[] strArr = new string[session.Count]; session.CopyTo(strArr, 0);//拷贝的是由key组成的字符串数组到strArr中 session.Remove("two");//按Key标识删除一个session对象 session.RemoveAt(0);//按索引号删除一个session对象 session.Clear(); session.RemoveAll();//删除所有session对象 session.Abandon();//取消当前会话 }
Cookie
Cookie:用于保存客户浏览器请求服务器页面的请求信息,其有效期可以人为设置,如果没有设置Cookie失效日期,它的生命周期保存到关闭浏览器为止,而且其存储的数据量很受限制,因此不要保存数据集及其他大量数据。而且Cookie以明文方式将数据信息保存在客户端的计算机中,因此最好不要保存敏感的未加密的数据。
看一下HttpCookie提供的功能:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
// 提供创建和操作各 HTTP Cookie 的类型安全方法。 public sealed class HttpCookie { // 创建并命名新的 Cookie。 // 参数: name: 新 Cookie 的名称。 public HttpCookie(string name); // 创建和命名新的 Cookie,并为其赋值。 // 参数: name: 新 Cookie 的名称。 value: 新 Cookie 的值。 public HttpCookie(string name, string value); // 获取或设置将此 Cookie 与其关联的域。 // 返回结果: 要将此 Cookie 与其关联的域名。 默认值为当前域。 public string Domain { get; set; } // 获取或设置此 Cookie 的过期日期和时间。 // 返回结果: 此 Cookie 的过期时间(在客户端)。 public DateTime Expires { get; set; } // 获取一个值,通过该值指示 Cookie 是否具有子键。 // 返回结果: 如果 Cookie 具有子键,则为 true;否则为 false。 默认值为 false。 public bool HasKeys { get; } // 获取或设置一个值,该值指定 Cookie 是否可通过客户端脚本访问。 // 返回结果: 如果 Cookie 具有 HttpOnly 特性且不能通过客户端脚本访问,则为 true;否则为 false。 默认值为 false。 public bool HttpOnly { get; set; } // 获取或设置 Cookie 的名称。 // 返回结果: 除非构造函数另外指定,否则默认值为 null 引用(在 Visual Basic 中为 Nothing)。 public string Name { get; set; } // 获取或设置要与当前 Cookie 一起传输的虚拟路径。 // 返回结果: 要与此 Cookie 一起传输的虚拟路径。 默认为 /,也就是服务器根目录。 public string Path { get; set; } // 获取或设置一个值,该值指示是否使用安全套接字层 (SSL)(即仅通过 HTTPS)传输 Cookie。 // 返回结果: 如果通过 SSL 连接 (HTTPS) 传输 Cookie,则为 true;否则为 false。 默认值为 false。 public bool Secure { get; set; } // 确定 cookie 是否允许参与输出缓存。 // 返回结果: 指定的true 输出缓存不会给出包含一个或多个对外 cookie 的 System.Web.HttpResponse 取消;否则 false。 public bool Shareable { get; set; } // 获取或设置单个 Cookie 值。 // 返回结果: Cookie 的值。 默认值为 null 引用(在 Visual Basic 中为 Nothing)。 public string Value { get; set; } // 获取单个 Cookie 对象所包含的键值对的集合。 // 返回结果: Cookie 值的集合。 public NameValueCollection Values { get; } // 获取 System.Web.HttpCookie.Values 属性的快捷方式。 此属性是为了与以前的 Active Server Pages (ASP) 版本兼容而提供的。 // 参数: key: Cookie 值的键(索引)。 // 返回结果: Cookie 值。 public string this[string key] { get; set; } }
看测试代码
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
//单个创建cookie HttpCookie cookie = new HttpCookie("MyCook"); cookie.Value = "我是单个cookie"; Response.AppendCookie(cookie); //单个获取cookie HttpCookie getCookie = Request.Cookies["MyCook"]; var coo = getCookie.Value; //下边是一组cookie //创建cookie HttpCookie cookie = new HttpCookie("MyCook");//初使化并设置Cookie的名称 DateTime dt = DateTime.Now; TimeSpan ts = new TimeSpan(0, 0, 1, 0, 0);//过期时间为1分钟 cookie.Expires = dt.Add(ts);//设置过期时间 cookie.Values.Add("coo1", "coo1_汉字"); cookie.Values.Add("coo2", "coo2_汉字"); Response.AppendCookie(cookie); //读取cookie HttpCookie getCookie = Request.Cookies["MyCook"];//获取客户端的Cookie对象 if (getCookie != null) { var coo1 = getCookie["coo1"]; var coo2 = getCookie["coo2"]; var coo = getCookie.Value; //输出全部的值 } //修改cookie HttpCookie alterCookie = Request.Cookies["MyCook"];//获取客户端的Cookie对象 if (alterCookie != null) { //修改Cookie的两种方法 alterCookie.Values["coo1"] = "new_coo1_汉字"; alterCookie.Values.Set("coo2", "new_coo2_汉字"); //往Cookie里加入新的内容 alterCookie.Values.Set("coo3", "coo3_汉字"); Response.AppendCookie(alterCookie); } //删除cookie HttpCookie delCookie = Request.Cookies["MyCook"];//获取客户端的Cookie对象 if (delCookie != null) { delCookie.Values.Remove("coo1");//移除键值为coo1的值 TimeSpan ts = new TimeSpan(-1, 0, 0, 0); delCookie.Expires = DateTime.Now.Add(ts);//删除整个Cookie,只要把过期时间设置为现在 Response.AppendCookie(delCookie); }
如果不够详细,不用着急 看这里 点~>_<~我
ViewState
ViewState:常用于保存单个用户的状态信息,可以保存大量的数据但是过多使用会影响应用程序的性能。所有Web服务器控件都使用ViewStat在页面回发期间保存自己的状态信息。每个控件都有自己的ViewState,不用时最好关闭以节省资源。通过给@Page指令添加"EnableViewState= false"属性可以禁止整个页面的ViewState。
定义ViewState属性
public int PageCount{
get{return (int)ViewState["PageCount"];}
set{ViewState["PageCount"]=value;}
}
使用ViewState的条件
如果要使用ViewState,则在ASPX页面中必须要有一个服务器端窗体标记(<form runat = "server">)。窗体字段是必须的,这样包含ViewState信息的隐藏字段才能被传回服务器。而且,该窗体还必须是服务器端的窗体,这样在服务器上执行该页面时,ASP.net页面框架才能添加隐藏字段。
page的EnableViewState 属性值为true
控件的EnableViewState 属性值为 true
ViewState需要注意的地方
a. 当存在页面回传时,不需要维持控件的值就要把ViewState 禁止。
b. ViewState的索引是大小写敏感的。
c. ViewState不是跨页面的。
d. 为了能保存在 ViewState中,对象必须是可流化或者定义了TypeConverter。
e. 控件 TextBox 的 TextMode 属性设置为 Password时,它的状态将不会被保存在 ViewState 中,这应该是出于安全性的考虑。
f. 在页面没有回传或重定向或在回传中转到(transfer)其他页面时不要使用 ViewState。
g. 在动态建立控件时要小心它的 ViewState。
h. 当禁止一个程序的 ViewState 时,这个程序的所有页面的 ViewState 也被禁止了。
i. 只有当页面回传自身时ViewState 才是持续的。
设置ViewState
ViewState可以在控件,页,程序,全局配置中设置。缺省情况下 EnableViewState 为 true 。如果要禁止所有页面 ViewState 功能,可以在程序配置中把 EnableViewState 设为 false 。
PS:上边简单的介绍了一下 ViewState 如果想深入研究点开下边的链接:ASP.NETViewState初探 .Net ViewState的实现
扩展 用static做缓存:
static 修饰符声明属于类型本身而不是属于特定对象的静态成员。 static 修饰符可用于类、字段、方法、属性、运算符、事件和构造函数,但不能用于索引器、析构函数或类以外的类型。
静态查询 Static Cache
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
public class AreaDal { private static List<Model.Area> _allArea; public static List<Model.Area> AllArea { get { return _allArea ?? new AreaDal().GetAllArea(); } } /// <summary> /// 获取地区信息集合 /// </summary> public List<Model.Area> GetAllArea() { return _allArea ?? (_allArea = GetAllEntitys()); } /// <summary> /// 根据ID获取地区详情 /// </summary> /// <param name="id">本级ID</param> /// <returns></returns> public Model.Area GetAreaById(int id) { if( id <= 0 ) return null; return AllArea.FirstOrDefault(m => m.ID == id); } /// <summary> /// 根据父级ID获取二级地区ID数组 /// </summary> /// <param name="id">本级ID</param> /// <returns></returns> public static int[] GetIds(int id) { var area = AllArea.FirstOrDefault(m => m.ID == id); if (id == 0 || area == null) { return AllArea.Select(m => m.ID).ToArray(); } if (area.Parent_id == 0) { return AllArea.Where(m => m.ID == id || m.Parent_id == id).Select(m => m.ID).ToArray(); } return new[] { id }; } /// <summary> /// 从数据库查出地区集合 /// </summary> public override List<Model.Area> GetAllEntitys() { const string cmdStr = "select * from Area"; return SqlHelper.GetEntitys<Model.Area>(connStr, cmdStr); } }
自定义存取 Static Cache
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
public static class StaticCache { //这里可以自定义类型 public static Dictionary<int, CatchItem> _siteCach; static StaticCache() { _siteCach = new Dictionary<int, CatchItem>(); } //添加缓存数据 //Key和subkey 是标识 //data 是要缓存的数据 public static void AddCache(int Key, int subkey, object data) { key = key + subKey * 12; if (_siteCach.Count(o => o.Key == key) > 0) _siteCach.Remove(key); _siteCach.Add(key, new CatchItem() { DateTime = DateTime.Now, Data = data }); } //获取缓存数据 //Key和subkey 是标识 //minutes 设置过期时间 从创建到使用 按分钟数计算 public static Object GetCache(int Key, int subkey, int minutes) { key = key + subKey * 12; if (_siteCach.Count(o => o.Key == key) > 0 && (DateTime.Now - _siteCach.FirstOrDefault(o => o.Key == key).Value.DateTime).TotalMinutes < minutes) return _siteCach.FirstOrDefault(o => o.Key == key).Value.Data; _siteCach.Remove(key); return null; } } //自定义缓存数据类型 public class CatchItem { public DateTime DateTime { get; set; } public ActionResult Result { get; set; } public Object Data { get; set; } }
可以去了解巩固一下静态与非静态的区别 看这里
先到这里吧 下一篇文章总结介绍Cache缓存和OutputCache及分布式缓存 东西虽然很基础 但是深入进去你会发现关联的东西太多了 想要学好 还是得多思考 多使用 多扩展