ASP.NET状态管理的总结

2024-09-07 13:18
文章标签 总结 管理 状态 asp net

本文主要是介绍ASP.NET状态管理的总结,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

由于HTTP协议的无状态特性,导致在ASP.NET编程中,每个请求都会在服务端从头到执行一次管线过程, 对于ASP.NET页面来说,Page对象都会重新创建,所有控件以及内容都会重新生成, 因此,如果希望上一次的页面状态能够在后续页面中保留,则必需引入状态管理功能。

  ASP.NET为了实现状态管理功能,提供了8种方法,可帮助我们在页面之间或者整个用户会话期间保留状态数据。 这些方法分为二类:视图状态、控件状态、隐藏域、Cookie 和查询字符串会以不同方式将数据发送到客户端上。 而应用程序状态、会话状态和配置文件属性(Profile)则会将数据存储到服务端。 虽然每种方法都有不同的优点和缺点,对于小的项目来说,可以选择自己认为最容易使用的方法, 然而,对于有着较高要求的程序,尤其是对于性能与扩展性比较关注的程序来说, 选择不同的方法最终导致的差别可能就非常大了。

  在这篇博客中,我将谈谈自己对ASP.NET状态管理方面的一些看法。
  注意:本文的观点可能并不合适开发小型项目,因为我关注的不是易用性。

  hidden-input

  hidden-input 这个名字我是取的,表示所有type="hidden"的input标签元素。 在中文版的MSDN中,也称之为 隐藏域 。 hidden-input通常存在于HTML表单之内,它不会显示到页面中, 但可以随表单一起提交,因此,经常用于维护当前页面的相关状态,在服务端我们可以使用Request.Form[]来访问这些数据。

  一般说来,我通常使用hidden-input来保存一些中间结果,用于在多次提交中维持一系列状态, 或者用它来保存一些固定参数用来提交给其它页面(或网站)。 在这些场景中,我不希望用户看到这些数据,因此,使用hidden-input是比较方便的。

  关于表单的更多介绍可参考我的博客:细说 Form (表单)

  在ASP.NET WebForm框架中,我们可以使用HiddenField控件来创建一个hidden-input控件,并可以在服务端操作它, 还可以直接以手写的方式使用隐藏域,例如:

<input type="hidden" name="hidden-1" value="aaaaaaa" />
<input type="hidden" name="hidden-2" value="bbbbbbb" />
<input type="hidden" name="hidden-3" value="ccccccc" />

  另外,我们还可以调用ClientScript.RegisterHiddenField()方法来创建隐藏域:

ClientScript.RegisterHiddenField("hidden-4", "ddddddddd");

  输出结果:

<input type="hidden" name="hidden-4" id="hidden-4" value="ddddddddd" />

  这三种方法对于生成的HTML代码来说,主要差别在于它们出现位置不同:
  1. HiddenField控件:由HiddenField的出现位置来决定(在form内部)。
  2. RegisterHiddenField方法:在form标签的开头位置。
  3. hidden-input:你写在哪里就是哪里。

  优点:
  1. 不需要任何服务器资源:隐藏域随页面一起发送到客户端。
  2. 广泛的支持:几乎所有浏览器和客户端设备都支持具有隐藏域的表单。
  3. 实现简单:隐藏域是标准的 HTML 控件,不需要复杂的编程逻辑。

  缺点:
  1. 不能在多页面跳转之间维持状态。
  2. 用户可见,保存敏感数据时需要加密。

  QueryString

  查询字符串是存在于 URL 结尾的一段数据。下面是一个典型的查询字符串示例(红色部分文字):

http://www.abc.com/demo.aspx?k1=aaa&k2=bbb&k3=ccc

  查询字符串经常用于页面的数据过滤,例如:
  1. 给列表页面增加分页参数,list.aspx?page=2
  2. 给列表页面增加过虑范围,Product.aspx?categoryId=5
  3. 显示特定记录,ProductInfo.aspx?page=3

  关于查询字符串的用法,我补充二点:
  1. 可以调用HttpUtility.ParseQueryString()来解析查询字符串。
  2. 允许参数名重复:list.aspx?page=2&page=3,因此在修改URL参数时,使用替换方式而不是追加。
  关于参数重名的读取问题,请参考我的博客:细说 Request[]与Request.Params[]

  优点:
  1. 不需要任何服务器资源:查询字符串的数据包含在每个URL中。
  2. 广泛的支持:几乎所有的浏览器和客户端设备均支持使用查询字符串传递参数值。
  3. 实现简单:在服务端直接访问Request.QueryString[]可读取数据。
  4. 页面传值简单:<a href="url">或者 Response.Redirect(url) 都可以实现。

  缺点:
  1. 有长度限制。
  2. 用户可见,不能保存敏感数据。

  Cookie

  由于HTTP协议是无状态的,对于一个浏览器发出的多次请求,WEB服务器无法区分它们是不是来源于同一个浏览器。所以,需要额外的数据用于维护会话。 Cookie 正是这样的一段随HTTP请求一起被传递的额外数据。 Cookie 是一小段文本信息,它的工作方式就是伴随着用户请求和页面在 Web 服务器和浏览器之间传递。Cookie 包含每次用户访问站点时 Web 应用程序都可以读取的信息。

  与hidden-input, QueryString相比,Cookie有更多的属性,许多浏览器可以直接查看这些信息:

  由于Cookie拥有这些属性,因此在客户端状态管理中可以实现更多的功能,尤其是在实现客户端会话方面具有不可替代的作用。

  关于Cookie的更多讲解,请参考我的另一篇博客:细说Cookie

  优点:
  1. 可配置到期规则:Cookie可以在客户端长期存在,也可以在浏览器关闭时清除。
  2. 不需要任何服务器资源:Cookie 存储在客户端。
  3. 简单性:Cookie 是一种基于文本的轻量结构,包含简单的键值对。
  4. 数据持久性:与其它的客户端状态数据相比,Cookie可以实现长久保存。
  5. 良好的扩展性:Cookie的读写要经过ASP.NET管线,拥有无限的扩展性。

  这里我要解释一下Cookie 【良好的扩展性】是个什么概念,比如:
  1. 我可以实现把Cookie保存到数据库中而不需要修改现有的项目代码。
  2. 把SessionId这样由ASP.NET产生的临时Cookie让它变成持久保存。

  缺点:
  1. 大小受到限制。
  2. 增加请求头长度。
  3. 用户可见,保存敏感数据时需要加密。

  ApplicationState

  应用程序状态是指采用HttpApplicationState实现的状态维持方式,使用代码如下:

Application.Lock();
Application["PageRequestCount"] = ((int)Application["PageRequestCount"]) + 1;
Application.UnLock();

  对于这种方法,我不建议使用,因为:
  1. 与使用静态变量差不多,直接使用静态变量可以不需要字典查找。
  2. 选择强类型的集合或者变量可以避免装箱拆箱。

  ViewState,ControlState

  视图状态,控件状态,二者是类似,在页面中表现为一个hidden-input元素:

<input type="hidden" name="__VIEWSTATE" id="__VIEWSTATE" value="......................" />

  控件状态是ASP.NET 2.0中引入,与视图状态相比,它不允许关闭。
  由于它们使用方式一致,而且视图状态是基于控件状态的实现逻辑,所以我就不区分它们了。

  在ASP.NET的早期,微软为了能帮助广大开发人员提高开发效率,引用入一大批的服务端控件,并为了能将事件编程机制引入ASP.NET中,又发明了ViewState。

  这种方式虽然可以简化开发工作量,然而却有一些限制和缺点:
  1. 视图状态的数据只能用于回发(postback)。
  2. 视图状态的【滥用】容易导致生成的HTML较大,这会引起一个恶性循环:
    a. 过大的ViewState在序列化过程中会消耗较多的服务器CPU资源,
    b. 过大的ViewState最终生成的HTML输出也会很大,它会浪费服务端网络资源,
    c. 过大的ViewState输出导致表单在下次提交时,会占用客户端网络资源。
    d. 过大的ViewState数据上传到服务端后,反序列化又会消耗较多的服务器CPU资源。
  因此,整个交互过程中,用户一直在等待,用户体验极差。

  在ASP.NET兴起的年代,ViewState绝对是个了不起的发明。
  然而,现在很多关于ASP.NET性能优化的方法中,都会将【关闭ViewState】放在头条位置。
  为什么会这样呢,大家可以自己思考一下了。

  有些人认为:我现在做的程序只是在局域网内使用,使用ViewState完全没有问题!
  然而,那些人或许没有想过:
  1. 未来用户可能会把它部署在互联网上运行(对于产品来说就是遇到大客户了)。
  2. 项目早期的设计与规划,对后期的开发与维护来说,影响是巨大的,因为许多基础部分通常是在早期开发的。
  当这二种情况的任何一种发生时,想再禁用ViewState,可能已经晚了。

  对于视图状态,我认为它解决的问题比它引入的问题要多要复杂,
  因此,我不想花时间整理它的优缺点,我只想说一句:把它关了,在web.config中关了。

  另外,我不排斥使用服务器控件,我认为:你可以使用服务端控件显示数据,但不要用它处理回发。

  如果你仍然认为视图状态是不可缺少的,那我还是建议你看看ASP.NET MVC框架,看看没有视图状态是不是照样可以写ASP.NET程序。

  Session

  Session是ASP.NET实现的一种服务端会话技术,它允许我们方便地在服务端保存与用户有关的会话数据。

  我认为Session只有一个优点:最简单的服务端会话实现方式。

  缺点:
  1. 当mode="InProc"时,容易丢失数据,为什么?因为网站会因为各种原因重启。
  2. 当mode="InProc"时,Session保存的东西越多,就越占用服务器内存,对于用户在线人数较多的网站,服务器的内存压力会比较大。
  3. 当mode="InProc"时,程序的扩展性会受到影响,原因很简单:服务器的内存不能在多台服务器间共享。
  4. 当采用进程外模式时,在每次请求中,不管你用不用会话数据,所有的会话数据都为你准备好了(反序列化),这其实很是浪费资源的。
  5. 如果你没有关闭Session,SessionStateModule就一直在工作中,尤其是全采用默认设置时,会对每个请求执行一系列的调用,浪费资源。
  6. 阻塞同一客户端发起的多次请求(默认方式)。
  7. 无Cookie会话可能会丢失数据(重新生成已过期的会话标识符)。

  Session的这些缺点也提醒我们:
  1. 当网站的在线人数较多时,一定不要用Session保存较大的对象。
  2. 在密集型的AJAX型网站或者大量使用iframe的网站中,要关注Session可能引起的服务端阻塞问题。
  3. 当采用进程外模式时,不需要访问Session的页面,一定要关闭,否则会浪费服务器资源。

  如果想了解更多的Session特点,以及我对Session的看法,可以浏览我的博客:Session,有没有必要使用它?

  Session的本质有二点:
  1. SessionId + 服务端字典:服务端字典保存了某个用户的所有会话数据。
  2. 用SessionId识别不同的客户端:SessionId通常以Cookie形式发送到客户端。

  我认为了解Sesssion本质非常有用,因为可以借鉴并实现自己的服务端会话方法。

  关于Session我还想说一点:
  有些新手喜欢用Session来实现身份认证功能,这是一种【不正确】的方法。
  如果你的ASP.NET应用程序需要身份认证功能,请使用 Forms身份认证 或者 Windows身份认证

  Profile

  Profile 在中文版的MSDN中被称为 配置文件属性,这个功能是在 ASP.NET 2.0 中引入的。

  ASP.NET提供这个功能主要是为了简化与用户相关的个性化信息的读写方式。
  简化主要体现在3个方面:
  1. 自动与某个用户关联,已登录用户或者未登录都支持。
  2. 不需要我们设计用户的个性化信息的保存表结构,只要修改配置文件就够了。
  3. 不需要我们实现数据的加载与保存逻辑,ASP.NET框架替我们实现好了。

  为了使用Profile,我们首先在web.config中定义所需要的用户个性化信息:

<profile><properties><add name="Address"/><add name="Tel"/></properties>
</profile>

  然后,就可以在页面中使用了:

  为什么会这样呢?
  原因是ASP.NET已经根据web.config为我们创建了一个新类型:

using System;
using System.Web.Profile;
public class ProfileCommon : ProfileBase
{public ProfileCommon();public virtual string Address { get; set; }public virtual string Tel { get; set; }public virtual ProfileCommon GetProfile(string username);
}

  有了这个类型后,当我们访问HttpContext.Profile属性时,ASP.NET会创建一个ProfileCommon的实例。 也正是由于Profile的强类型机制,在使用Profile时才会有智能提示功能。

  如果我们希望为未登录的匿名用户也提供这种支持,需要将配置修改成:

<profile><properties><add name="Address" allowAnonymous="true" /><add name="Tel" allowAnonymous="true"/></properties>
</profile>
<anonymousIdentification enabled="true" />

  Profile中的每个属性还允许指定类型和默认值,以及序列化方式,因此,扩展性还是比较好的。

  尽管Profile看上去很美,然而,使用Profile的人却很少。
  比如我就不用它,我也没见有人有过它。
  为什么会这样?

  我个人认为:它与MemberShip一样,是个鸡肋。
  通常说来,我们会为用户信息创建一张User表,增加用户信息时,会通过增加字段的方式解决。
  我认为这样集中的数据才会更好,而不是说,有一部分数据由我维护,另一部分数据由ASP.NET维护。
  另一个特例是:我们根本不创建User表,直接使用MemberShip,那么Profile用来保存MemberShip没有信息是有必要的。

  还是给Profile做个总结吧:
  优点:使用简单。
  缺点:不实用。

  各种状态管理的对比与总结

  前面分别介绍了ASP.NET的8种状态管理技术,这里打算给它们做个总结。

 

 客户端服务端
数据安全性
数据长度限制受硬件限制
占用服务器资源
集群扩展性

 

  表格中主要考察了数据保存与服务端水平扩展的相关重要指标。

  下面我来解释表格的结果。
  1. 客户端方式的状态数据(hidden-input, QueryString, Cookie):
    a. 数据对用户来说,可见可修改,因此数据不安全。
    b. QueryString, Cookie 都有长度限制。
    c. 数据在客户端,因此不占用服务端资源。这个特性对于在线人数很多的网站非常重要。
    d. 数据在客户端,因此和服务端没有耦合关系,WEB服务器可以更容易实现水平扩展。
  2. 服务端方式的状态数据(ApplicationState,ViewState,ControlState,Session,Profile):
    a. 数据对用户不可见,因此安全性好。(ApplicationState,Session,Profile)
    b. 数所长度只受硬件限制,因此,对于在线人数较多的网站,需谨慎选择。
    c. 对于存放在内存中的状态数据,由于不能共享内存,因此会限制水平扩展能力。
    d. 如果状态数据保存到一台机器,会有单点失败的可能,也会限制了水平扩展能力。

  从这个表格我们还可以得到以下结论:
  1. 如果很关注数据的安全性,应该首选服务端的状态管理方法。
  2. 如果你关注服务端的水平扩展性,应该首选客户端的状态管理方法。

  会话状态的选择

  接下来,我们再来看看会话状态,它与状态管理有着一些关系,属于比较类似的概念。

  谈到会话状态,首先我要申明一点:会话状态与状态不是一回事。

  本文前面所说的状态分为二种:
  1. 页面之间的状态。
  2. 应用程序范围内的状态。

  而会话状态是针对某个用户来说,他(她)在多次操作之间的状态。
  在用户的操作期间,有可能状态需要在页面之间持续使用,
  也有可能服务端程序做过重启,但数据仍然有效。
  因此,这种状态数据更持久。

  在ASP.NET中,使用会话状态有二个选择:Session 或者 Cookie 。
  前者由ASP.NET实现,并有可能依赖后者。
  后者则由浏览器实现,ASP.NET提供读写方法。

  那么到底选择哪个呢?
  如果你要问我这个问题,我肯定会说:我选 Cookie !

  下面是我选择Cookie实现会话状态的理由:
  1. 不会有服务端阻塞问题。
  2. 不占用服务端资源。
  3. 水平扩展没有限制。
  4. 也支持过期设置,而且更灵活。
  5. 可以在客户端直接使用会话数据。
  6. 可以实现更灵活的会话数据加载策略。
  7. 扩展性较好(源于ASP.NET管线的扩展性)

  如果选择使用Cookie实现会话状态,有3点需要特别注意:
  1. 不建议保存敏感数据,除非已加密。
  2. 只适合保存短小简单的数据。
  3. 如果会话数据较大,可以在客户端保存用户标识,由服务端实现数据的加载保存逻辑。

  或许有些人认为:每种技术都有它们的优缺点,有各自的适用领域。
  我表示赞同这句话。
  但是,我们要清楚一点:每个项目的规模不一样,性能以及扩展性要求也不同。
  对于一个小的项目来说,选择什么方法都不是问题,
  但是,对于规模较大的项目,我们一定需要取舍。
  取舍的目标是:包装越少越好,因为人家做了过多的包装,就会有较多的限制,
  所以,不要只关注现在的调用是否方便,其实只要你愿意包装,你也可以让复杂的调用简单化。

  改变开发方式,发现新方法

  回想一下:为什么在ASP.NET中需要状态管理?
  答:因为与HTTP协议有关,服务端没有保存每个请求的上次页面状态。

  为什么Windows计算器(这类)程序不用考虑会话问题呢?
  答:因为这类程序的界面不需要重新生成,任何变量都可表示状态。

  再来看这样一个场景:

  图片左边是一个列表页面,允许调整每条记录的优先级,但是有2个要求:
  1. 在移动每条记录时,必须输入一个调整理由。
  2. 只要输入理由后,那条记录可以任意调整多次。

  显然,完成这个任务必须要有状态才能实现。

  面对这个问题,你可以思考一下:选择哪种ASP.NET支持的状态管理方法都很麻烦。

  怎么办?

  我的解决方法:创建一个JavaScript数组,用每个数组元素保存每条记录的状态,
  所有用户交互操作用AJAX方式实现,这样页面不会刷新,JavaScript变量中的状态一直有效。
  因此,很容易就能解决这个问题。

  这个案例也提醒我们:当发现ASP.NET提供的状态管理功能全部不合适时, 我们需要改变开发方式了。

  为什么WEB编程都有【无状态】问题,而桌面程序没有?
  我认为与HTTP协议有关,但没有绝对的关系。
  只要你能保证页面不刷新,也能像桌面程序那样,用JavaScript变量就能维护页面状态。

 

这篇关于ASP.NET状态管理的总结的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1145161

相关文章

HarmonyOS学习(七)——UI(五)常用布局总结

自适应布局 1.1、线性布局(LinearLayout) 通过线性容器Row和Column实现线性布局。Column容器内的子组件按照垂直方向排列,Row组件中的子组件按照水平方向排列。 属性说明space通过space参数设置主轴上子组件的间距,达到各子组件在排列上的等间距效果alignItems设置子组件在交叉轴上的对齐方式,且在各类尺寸屏幕上表现一致,其中交叉轴为垂直时,取值为Vert

学习hash总结

2014/1/29/   最近刚开始学hash,名字很陌生,但是hash的思想却很熟悉,以前早就做过此类的题,但是不知道这就是hash思想而已,说白了hash就是一个映射,往往灵活利用数组的下标来实现算法,hash的作用:1、判重;2、统计次数;

hdu1043(八数码问题,广搜 + hash(实现状态压缩) )

利用康拓展开将一个排列映射成一个自然数,然后就变成了普通的广搜题。 #include<iostream>#include<algorithm>#include<string>#include<stack>#include<queue>#include<map>#include<stdio.h>#include<stdlib.h>#include<ctype.h>#inclu

hdu1565(状态压缩)

本人第一道ac的状态压缩dp,这题的数据非常水,很容易过 题意:在n*n的矩阵中选数字使得不存在任意两个数字相邻,求最大值 解题思路: 一、因为在1<<20中有很多状态是无效的,所以第一步是选择有效状态,存到cnt[]数组中 二、dp[i][j]表示到第i行的状态cnt[j]所能得到的最大值,状态转移方程dp[i][j] = max(dp[i][j],dp[i-1][k]) ,其中k满足c

综合安防管理平台LntonAIServer视频监控汇聚抖动检测算法优势

LntonAIServer视频质量诊断功能中的抖动检测是一个专门针对视频稳定性进行分析的功能。抖动通常是指视频帧之间的不必要运动,这种运动可能是由于摄像机的移动、传输中的错误或编解码问题导致的。抖动检测对于确保视频内容的平滑性和观看体验至关重要。 优势 1. 提高图像质量 - 清晰度提升:减少抖动,提高图像的清晰度和细节表现力,使得监控画面更加真实可信。 - 细节增强:在低光条件下,抖

poj 1258 Agri-Net(最小生成树模板代码)

感觉用这题来当模板更适合。 题意就是给你邻接矩阵求最小生成树啦。~ prim代码:效率很高。172k...0ms。 #include<stdio.h>#include<algorithm>using namespace std;const int MaxN = 101;const int INF = 0x3f3f3f3f;int g[MaxN][MaxN];int n

软考系统规划与管理师考试证书含金量高吗?

2024年软考系统规划与管理师考试报名时间节点: 报名时间:2024年上半年软考将于3月中旬陆续开始报名 考试时间:上半年5月25日到28日,下半年11月9日到12日 分数线:所有科目成绩均须达到45分以上(包括45分)方可通过考试 成绩查询:可在“中国计算机技术职业资格网”上查询软考成绩 出成绩时间:预计在11月左右 证书领取时间:一般在考试成绩公布后3~4个月,各地领取时间有所不同

git使用的说明总结

Git使用说明 下载安装(下载地址) macOS: Git - Downloading macOS Windows: Git - Downloading Windows Linux/Unix: Git (git-scm.com) 创建新仓库 本地创建新仓库:创建新文件夹,进入文件夹目录,执行指令 git init ,用以创建新的git 克隆仓库 执行指令用以创建一个本地仓库的

安全管理体系化的智慧油站开源了。

AI视频监控平台简介 AI视频监控平台是一款功能强大且简单易用的实时算法视频监控系统。它的愿景是最底层打通各大芯片厂商相互间的壁垒,省去繁琐重复的适配流程,实现芯片、算法、应用的全流程组合,从而大大减少企业级应用约95%的开发成本。用户只需在界面上进行简单的操作,就可以实现全视频的接入及布控。摄像头管理模块用于多种终端设备、智能设备的接入及管理。平台支持包括摄像头等终端感知设备接入,为整个平台提

如何在Visual Studio中调试.NET源码

今天偶然在看别人代码时,发现在他的代码里使用了Any判断List<T>是否为空。 我一般的做法是先判断是否为null,再判断Count。 看了一下Count的源码如下: 1 [__DynamicallyInvokable]2 public int Count3 {4 [__DynamicallyInvokable]5 get