ASP.NET 系列_08_编程指南(四)

ASP.NET 安全性

安全性

实现网站的安全性关系到如下几方面:

  • 身份认证:即确认用户身份和真实性的过程。ASP.NET 中提供了四种类型的认证:
    • Windows 认证
    • 表单认证
    • 身份验证
    • 自定义认证
  • 授权:即定义并为特定用户分配特定角色的过程。
  • 机密性:包括对客户端浏览器和网络服务器的加密。
  • 完整性:保持数据完整性。例如,实现数字签名。

基于表单的认证

一般来讲,基于表单的认证包括编辑网络配置文件以及具有验证码的注册页面。
网络配置文件可由如下代码编写:

<configuration>

<system.web>
    <authentication mode="Forms">
        <forms loginUrl ="login.aspx"/>
    </authentication>

    <authorization>
        <deny users="?"/>
    </authorization>
</system.web>
...
...
</configuration>

上面的代码段中提及的 login.aspx 页面可能会包含如下代码,包含验证用的用户名和密码在文件之后很难编码进去。

protected bool authenticate(String uname, String pass)
{
    if(uname == "Tom")
    {
        if(pass == "tom123")
            return true;
    }

    if(uname == "Dick")
    {
        if(pass == "dick123")
            return true;
    }

    if(uname == "Harry")
    {
        if(pass == "har123")
            return true;
    }

    return false;
}

public void OnLogin(Object src, EventArgs e)
{
    if (authenticate(txtuser.Text, txtpwd.Text))
    {
        FormsAuthentication.RedirectFromLoginPage(txtuser.Text, chkrem.Checked);
    }
    else
    {
        Response.Write("Invalid user name or password");
    }
}

注意到 FormsAuthentication 类是用于认证过程的。

然而,不用写任何代码 Visual Studio 就能够通过网站管理工具轻松地无缝实现用户创建、身份认证和授权。这种工具能够实现用户和角色的创建。

除此之外,ASP.NET 有现成的登录控制系列,可以为你控制执行所有的工作。

基于表单的安全性的实现

为了建立基于表单的认证,你需要做到如下几点:

  • 支持认证过程的用户数据库
  • 一个使用数据库的网站
  • 用户账户
  • 角色
  • 用户活动和群体活动的限制
  • 一个显示用户状态及其他信息的用户页面
  • 允许用户登录、找回密码、修改密码的登录界面。

为了创建一个用户,需要采取以下步骤:

扫描二维码关注公众号,回复: 2618486 查看本文章

第一步:选择网站 -> 配置 ASP.NET 以打开网络应用管理工具。

第二步:点击安全选项。

security_tab.jpg

第三步:选择 'Forms based authentication' 选项,以将认证类型设定为 'From the Internet'。

authentication_type.jpg

第四步:点击 'Create Users'。如果你已经创建了角色,你正好可以在这一步把角色分配给该用户。

create_users_link.jpg

第五步:创建一个网站,并添加如下页面:

  • 欢迎页面
  • 登录页面
  • 注册页面
  • 找回密码页面
  • 修改密码页面

第六步:在欢迎页面的登录部分设置一个登录状态控件。包含两个标准框:LoggedIn 和 LoggedOut。

LoggedIn 有查看已经登录用户的选项,LoggedOut 内有查看已经退出用户的选项。你可以在属性窗口里改变登录和退出的文本属性。

login_status_control.jpg

第七步:在 LoginStatus 控件的下面设置一个 LoginView 控件。你可以在此设置一些能反应用户是否已经登录的其他文本或其他控件(如超链接、按钮等)。

这个控件有两个标准框: Anonymous 框和 LoggedIn 框。选择每种视图,并为用户编写一些文本,以作为选择标准框时要显示的内容。文本应该被放在如下图中标红的区域。

login_view_control.jpg

第八步:由开发者创建应用用户。你也许想要允许游客也能够创建一个用户账户。要实现这个,你可以在 LoginView 控件下添加一个可以转到注册页面的链接。

第九步:在注册页面设置一个 CreateUserWizard 控件。设置这个控件的 ContinueDestinationPageUrl 属性,以保证能够转到欢迎页面。

createuserwizard_control.jpg

第十步:创建登录页面。在这个页面上设置一个 Login 控件。 LoginStatus 控件会自动地连接到登录页面。在网络配置文件里做如下改动可以改变这种默认设置。

例如,如果你把你的登录页面命名为 signup.aspx ,可以在网络配置文件的 部分添加如下几行代码。

<configuration>
    <system.web>
        <authentication mode="Forms">
            <forms loginUrl ="signup.aspx" defaultUrl = “Welcome.aspx†/>
        </authentication>
    </system.web>
</configuration>

第十一步:用户经常会忘记密码。PasswordRecovery 控件帮助用户重新获得登录这个账户。选择登录控件。打开它的小标签,并选择 'Convert to Template'。

通过自定义这个控件的用户界面,在登录按钮下方放置一个超链接控件,这个控件应该是能够链接到找回密码页面的。

passwordrecovery_control.jpg

第十二步:在找回密码页面设置一个 PasswordRecovery 控件。这个控件需要邮件服务器把密码发送给用户。

passwordrecovery_control2.jpg

第十三步:在欢迎页面的 LoginView 控件的 LoggedIn 框内设置一个转到修改密码页面的链接。

changepassword_control.jpg

第十四步:在修改密码页面设置一个 ChangePassword 控件,这个控件有两种视图:

changepassword_control2.jpg

现在运行这个应用,观察不同的安全操作。
可以回到网络应用管理工具,点击安全选项,来创建角色。点击 'Create Roles' 为这个应用来创建一些角色。

web_application_administration.jpg

点击 'Manage Users',可以给用户分配角色。

manage_users.jpg

IIS 认证:SSL

安全套接层(SSL)是用来确保安全连接的协议。通过使用 SSL,浏览器会把送到服务器的所有数据加密,并解密来自服务器的所有数据。与此同时,服务器也会对俩字浏览器的所有数据进行加解密。

安全连接的 URL 使用的是 HTTPS 协议而不是 HTTP 协议。一个很小的加锁也会被使用了安全连接的浏览器显示出来。当浏览器使用 SSL 主动地与服务器进行交流时,服务器会发送一个安全证书以对服务器本身进行认证。

要想使用 SSL ,你需要从一个可以信任的认证机构(CA)购买一个数字安全证书,并在网络服务器上安装这个证书。以下是一些可以信任的,有较好名誉认证机构:

  • www.verisign.com
  • www.geotrust.com
  • www.thawte.com

SSL 是建立在所有主要的浏览器和服务器上的。要启用 SSL,你需要安装数字证书。不同数字证书的强度不同,是根据加密过程中产生的密钥长度而有所区别。密钥越长,证书就越安全,连接也就越安全。

强度 描述
40 比特 支持大多数浏览器但是很容易破解。
56 比特 比 40 比特的更健壮。
128 比特 很难破解,但并不是所有的浏览器都支持。

ASP.NET 数据缓存

数据缓存

什么是缓存?

缓存是一种将经常使用的数据/信息存储在内存中的技术,这样,下次需要相同的数据/信息时,可以直接从内存检索,而不是再从应用程序中生成。

缓存在用于提高 ASP 性能方面是非常重要的,因为 ASP 的页面和控件是都动态生成的。这对于交互相关的数据是极其重要的,因为响应时间是很宝贵的。

在需要快速访问的媒体,如计算机的随机存取存储器,缓存放置了被频繁使用的数据。ASP 的运行时间包含一个叫做缓存的 CLR 对象的键值对。它位于应用程序内,并且通过 HttpContext 和 System.Web.UI.Page 可用。

在某些方面,缓存和存储状态对象有相似之处。然而,状态对象的存储信息是确定的,比如,你可以计算存储在状态对象的数据,但是缓存的数据是不确定的。

在下列情况里,数据是不可用的:

  • 如果它的生命周期已结束,
  • 如果该应用释放了它的内存,
  • 如果由于某些原因缓存没有被替换。

您可以使用一个索引器在缓存中访问项目,并且有可能控制缓存中对象的生命周期和设置缓存的对象及其物理资源之间的联系。

ASP.NET 中的缓存

ASP提供如下几种不同类型的缓存:

  • 输出缓存:输出缓存可以存储最后显现的网页的副本,或者是发送到客户机的部分页面。下次客户机请求该页面时,这个页面的缓存副本就会被发送给客户机,而不是重新生成这个页面,这样一来就节省了时间。
  • 数据缓存:数据缓存是指从数据源缓存数据。只要缓存没被替换,那么再请求该数据时就会从缓存中获取。当缓存被替换的时候,会从数据源中获取新数据,缓存也会被再次充满。
  • 对象缓存:对象缓存是缓存页面的对象,比如数据绑定控件等。缓存的数据放在服务器的内存。
  • 类缓存:网页或 Web 服务是第一次运行时在组装编译成页类。然后组装会在服务器缓存。当下次请求该页面或者服务,就会使用缓存的装配。当改变源代码时,CLR 重新编译程序集。
  • 配置缓存:应用程序配置信息存储在一个配置文件。配置缓存存储在服务器内存配置信息。

在本教程中,我们将考虑输出缓存,数据缓存和对象缓存。

输出缓存

呈现一个页面可能涉及一些复杂的过程,如,数据库访问,呈现复杂的控件等。输出缓存允许通过在内存中缓存数据,而绕过往返服务器。甚至可以缓存整个页面。

OutputCache 指令负责输出缓存。它启用输出缓存,并对其行为提供一定程度的控制。

OutputCache 指令的语法:

<%@ OutputCache Duration="15" VaryByParam="None" %>

把这个指令放在页面指令下。这告诉环境需要缓存页面,持续 15 秒。以下页面加载事件处理程序将帮助确认页面是否已被缓存完毕。

protected void Page_Load(object sender, EventArgs e)
{
    Thread.Sleep(10000);  
    Response.Write("This page was generated and cache at:" +
    DateTime.Now.ToString());
}

Thread.Sleep()方法使特定时间内的进程停止。在这个例子中,线程停止了 10 秒钟,因此,当页面第一次下载时,需要花费 10 秒钟的时间。然而,下次刷新页面的时候,就不会花费时间了,因为这个页面已经从缓存中获取了,不要再下载。

当帮助控制输出缓存的行为 OutputCache 指令有以下特性:

属性 描述
DiskCacheable true/false 描述输出是否可以写入带有缓存的磁盘。
NoStore true/false 描述 "no store" 缓存头部是否被发送。
CacheProfile 字符串名 存储在 web.config 中的缓存配置文件名字。
VaryByParam None
*
参数名
GET 请求中使用分号分隔的字符串值或者是 POST 请求中的变量值。
VaryByHeader *
头文件名
可能是由客户端提交的用分号分隔的指定头的字符串。
VaryByCustom 浏览器
自定义字符串
通知 ASP.NET 通过浏览器名字版本或者客户端字符串改变输出缓存。
Location 任何
客户端
下载流
服务器
None
任何:页面可能缓存在任何位置
客户端:缓存内容包含在浏览器中
下载流:缓存内容保存在下载流和服务器中
服务器:缓存仅保存在服务器之中
None:不允许缓存。
Duration 数字 被缓存页面或者操作的秒数。

让我们为前面的示例添加一个文本框和一个按钮,并添加这个按钮的事件处理程序。

protected void btnmagic_Click(object sender, EventArgs e)
{
    Response.Write("<br><br>");
    Response.Write("<h2> Hello, " + this.txtname.Text + "</h2>");
}

改变 OutputCache 指令:

<%@ OutputCache Duration="60" VaryByParam="txtname" %>

程序执行的时候,ASP 在文本框中依据名字缓存页面。

数据缓存

数据缓存的主要方面是数据源控件缓存。我们已经讨论了数据源控件代表一个数据源中的数据,如数据库或 XML 文件。这些控件从抽象类 DataSourceControl 中派生,并有以下继承属性以实现缓存:

  • 缓存期 — 为缓存数据的数据源计时。
  • 缓存期满策略 — 定义了当数据在缓存中过期时,缓存的行为。
  • 缓存值依赖 — 定义了一个控件值,这个控件可以在数据期满时自动将其移出缓存。
  • 启用缓存 — 可以确认是否缓存了数据。

实例

为了演示数据缓存,我们创建一个新的网站,在上面添加一个新的网络表单。在数据库中添加一个连接数据访问教程的 SqlDataSource 控件。

在这个实例中,我们给页面添加一个标签,这个标签可以显示页面的回应时间。

<asp:Label ID="lbltime" runat="server"></asp:Label>

除了这个标签,整个页面和数据访问教程是一样的。为这个页面添加一个事件处理器,来下载时间。

protected void Page_Load(object sender, EventArgs e)
{
    lbltime.Text = String.Format("Page posted at: {0}", DateTime.Now.ToLongTimeString());
}

设计的页面应该是如下这个样子的:

data_caching.jpg

当你第一次执行页面时,并没有发生什么不同。标签显示,每次刷新页面,页面会重新加载,而且在标签上会显示时间的变化。

接下来,把数据源控件的 EnableCaching 的属性设置为“真”,将 Cacheduration 属性设置为 “60”。它将实现缓存,缓存将每隔 60 秒到期。

每一次刷新,时间戳都会变化。但如果你在 60 秒之内改变表中的数据,在缓存到期之前将不会显示。

<asp:SqlDataSource ID = "SqlDataSource1" runat = "server" 
   ConnectionString = "<%$ ConnectionStrings: ASPDotNetStepByStepConnectionString %>" 
    ProviderName = "<%$ ConnectionStrings: ASPDotNetStepByStepConnectionString.ProviderName %>" 
    SelectCommand = "SELECT * FROM [DotNetReferences]"
    EnableCaching = "true" CacheDuration = "60">         
</asp:SqlDataSource>

对象缓存

对象缓存比其他缓存技术提供了更大的灵活性。你可以利用对象缓存在缓存中放置任何对象。对象也可以是任意类型的 — 数据类型,网络控件,类,数据设置对象等等。仅仅需要给这些项目分配一个值名,它们就可以被添加到缓存中,就像下面展示的这样:

Cache["key"] = item;

为了在缓存中插入对象, ASP 提供了 Insert() 方法。这种方法有四种重载版本。我们来看一下:

重载 描述
Cache.Insert((key, value); 以键值对的方式插入缓存,优先权和生命周期为默认 。
Cache.Insert(key, value, dependencies); 以键值对的方式插入缓存,优先权和生命周期为默认,和链接到其他文件或内容的缓存依赖,这样缓存修改就不再还有限的了。
Cache.Insert(key, value, dependencies, absoluteExpiration, slidingExpiration); 指出上述配置的有效期。
Cache.Insert(key, value, dependencies, absoluteExpiration, slidingExpiration, priority, onRemoveCallback); 与配置一起也允许设置缓存内容的优先权并委派,指出一种方法来调用当一个对象移除时。

动态生命周期使用于移除一个不作用于任何一个指定的时间跨度的缓存项。下面代码段用来保存一个具有 10 分钟滑动生命周期的无依赖的缓存项:

Cache.Insert("my_item", obj, null, DateTime.MaxValue, TimeSpan.FromMinutes(10));

实例

仅仅使用一个按钮和一个标签创建一个页面。在页面加载事件中写入如下代码:

protected void Page_Load(object sender, EventArgs e)
{
    if (this.IsPostBack)
    {
        lblinfo.Text += "Page Posted Back.<br/>";
    }
    else
    {
        lblinfo.Text += "page Created.<br/>";
    }

    if (Cache["testitem"] == null)
    {
        lblinfo.Text += "Creating test item.<br/>";
        DateTime testItem = DateTime.Now;
        lblinfo.Text += "Storing test item in cache ";
        lblinfo.Text += "for 30 seconds.<br/>";
        Cache.Insert("testitem", testItem, null, 
        DateTime.Now.AddSeconds(30), TimeSpan.Zero);
    }
    else
    {
        lblinfo.Text += "Retrieving test item.<br/>";
        DateTime testItem = (DateTime)Cache["testitem"];
        lblinfo.Text += "Test item is: " + testItem.ToString();
        lblinfo.Text += "<br/>";
    }

    lblinfo.Text += "<br/>";
}

当页面第一次加载时,会显示:

Page Created.
Creating test item.
Storing test item in cache for 30 seconds.

如果你在 30 秒钟内再次点击按钮,虽然页面被删除了,但是标签控件会从缓存中得到信息,如下所示:

Page Posted Back.
Retrieving test item.
Test item is: 14-07-2010 01:25:04

ASP.NET Web 服务

Web 服务

Web 服务是一个基于网络的功能,可被 web 应用通过 web 网络协议获取。web 服务开发主要包含以下三方面:

  • 创建 web 服务
  • 创建代理服务器
  • 使用 web 服务

创建 web 服务

一个 web 服务就是一个 web 应用,基本形式为一个类包含可以被其他应用调用的多个方法,它也采用隐藏代码结构例如 ASP.NET 网页,但它不存在用户接口。

为了更好地理解这个概念让我们创建一个提供股票价格信息的 web 服务。该服务的客户端可以通过股票的标签查询相关的名字和价格。为了简化这个例子,我们设置股票价格为固定值,保存在一个二维列表中。这个 web 服务包含三个方法:

  • 一个默认的 HelloWorld 方法
  • 一个 GetName 方法
  • 一个 GetPrice 方法

采取以下步骤创建该服务:

步骤 (1) : 在 Visual Studio 中选择 File -> New -> Web Site,然后选择 ASP.NET Web Service。

步骤 (2) : 一个名为 Service.asmx 的 web 服务文件和它的代码被隐藏,Service.cs 会在这个工程的 App_Code 路径下被创建。

步骤 (3) : 将文件名修改为 StockService.asmx 和 StockService.cs。

步骤 (4) : .asmx 文件简化了一个 WebService 指令如下:

<%@ WebService Language="C#" CodeBehind="~/App_Code/StockService.cs" 
Class="StockService" %> 

步骤 (5) : 打开 StockService.cs 文件,在该文件里生成的代码是 Hello World 服务的基础代码。默认的 web 服务代码如下:

    using System;
    using System.Collections;
    using System.ComponentModel;
    using System.Data;
    using System.Linq;

    using System.Web;
    using System.Web.Services;
    using System.Web.Services.Protocols;

    using System.Xml.Linq;

    namespace StockService
    {
       // <summary>
       // Summary description for Service1
       // <summary>

       [WebService(Namespace = "http://tempuri.org/")]
       [WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
       [ToolboxItem(false)]

       // To allow this Web Service to be called from script, 
       // using ASP.NET AJAX, uncomment the following line. 
       // [System.Web.Script.Services.ScriptService]

       public class Service1 : System.Web.Services.WebService
       {
          [WebMethod]

          public string HelloWorld()
          {
             return "Hello World";
          }
       }
    }

步骤 (6) : 修改文件内的代码增加一个存储了各股票标签,名称和价格的字符串的二维指针,并编写获取股票信息的两个 web 方法如下;

    using System;
    using System.Linq;

    using System.Web;
    using System.Web.Services;
    using System.Web.Services.Protocols;

    using System.Xml.Linq;

    [WebService(Namespace = "http://tempuri.org/")]
    [WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]

    // To allow this Web Service to be called from script, 
    // using ASP.NET AJAX, uncomment the following line. 
    // [System.Web.Script.Services.ScriptService]

    public class StockService : System.Web.Services.WebService
    {
       public StockService () {
      //Uncomment the following if using designed components 
      //InitializeComponent(); 
       }

       string[,] stocks =
       {
          {"RELIND", "Reliance Industries", "1060.15"},
          {"ICICI", "ICICI Bank", "911.55"},
          {"JSW", "JSW Steel", "1201.25"},
          {"WIPRO", "Wipro Limited", "1194.65"},
          {"SATYAM", "Satyam Computers", "91.10"}
       };

      [WebMethod]
       public string HelloWorld() {
          return "Hello World";
       }

      [WebMethod]
       public double GetPrice(string symbol)
       { 
          //it takes the symbol as parameter and returns price
          for (int i = 0; i < stocks.GetLength(0); i++)
          {
             if (String.Compare(symbol, stocks[i, 0], true) == 0)
             return Convert.ToDouble(stocks[i, 2]);
          }

          return 0;
       }

       [WebMethod]
       public string GetName(string symbol)
       {
          // It takes the symbol as parameter and 
          // returns name of the stock
          for (int i = 0; i < stocks.GetLength(0); i++)
          {
             if (String.Compare(symbol, stocks[i, 0], true) == 0)
             return stocks[i, 1];
          }

          return "Stock Not Found";
       }
    }

步骤 (7) : 运行 web 服务应用给出了一个 web 服务测试页面,我们可以在该页面测试服务方法。

步骤 (8) : 点击一个方法名字,确认它是否在正确运行。

步骤 (9) : 为检测 GetName 方法,提供已经被定义的股票标签中的一个,正确的话会返回相关股票的名称。

使用 Web 服务

为使用该 web 服务,我们在相同的解决方案(Solution)下创建一个网站,只需在解决方案管理器上右击该解决方案名字即可,web 服务调用的网页应具有一个控制管理以显示返回的结果和两个控制按钮,一个用于返回另一个用于开始调用服务。

web 应用的文件内容如下:

    <%@ Page Language="C#" AutoEventWireup="true" CodeBehind="Default.aspx.cs" Inherits="wsclient._Default" %>

    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

    <html xmlns="http://www.w3.org/1999/xhtml" >

       <head runat="server">
          <title>
             Untitled Page
          </title>
       </head>

       <body>

          <form id="form1" runat="server">
             <div>

                 <h3>Using the Stock Service</h3>

                 <br /> <br />

                 <asp:Label ID="lblmessage" runat="server"></asp:Label>

                 <br /> <br />

                 <asp:Button ID="btnpostback" runat="server" onclick="Button1_Click" Text="Post Back" style="width:132px" />

                 <asp:Button ID="btnservice" runat="server" onclick="btnservice_Click"  Text="Get Stock" style="width:99px" />

              </div>
          </form>

       </body>
    </html>

web 应用的代码如下:

    using System;
    using System.Collections;
    using System.Configuration;
    using System.Data;
    using System.Linq;

    using System.Web;
    using System.Web.Security;
    using System.Web.UI;
    using System.Web.UI.HtmlControls;
    using System.Web.UI.WebControls;
    using System.Web.UI.WebControls.WebParts;

    using System.Xml.Linq;

    //this is the proxy
    using localhost;

    namespace wsclient
    {
       public partial class _Default : System.Web.UI.Page
       {
           protected void Page_Load(object sender, EventArgs e)
           {
               if (!IsPostBack)
               {
                   lblmessage.Text = "First Loading Time: " +  DateTime.Now.ToLongTimeString
               }
               else
               {
                   lblmessage.Text = "PostBack at: " + DateTime.Now.ToLongTimeString();
               }
           }

           protected void btnservice_Click(object sender, EventArgs e)
           {
               StockService proxy = new StockService();
               lblmessage.Text = String.Format("Current SATYAM Price:{0}",
               proxy.GetPrice("SATYAM").ToString());
           }
      }
    }

创建代理服务器

代理服务器指的是一个 web 服务代码的代替者。在使用 web 服务之前,我们必须创建一个代理服务器。这个代理服务器是由客户端应用注册的。然后客户端应用实现调用 web 服务使之像在使用一个本地方法一样。

该代理服务器将调用,并用适当的格式将调用像发送 SOAP 请求一样发送到服务器。SOAP 支持简单对象访问协议(Simple Object Access Protocol)。该协议适用于 web 服务数据交换。

当此服务器响应并返回一个 SOAP 包给客户端时,代理服务器将一切呈现给客户端应用程序。

使用 btnservice_click 调用 Web 服务之前,Web 应用应该被添加到应用程序。这将透明地创建一个代理类,可由 btnservice_click 事件使用。

    protected void btnservice_Click(object sender, EventArgs e)
    {
       StockService proxy = new StockService();
       lblmessage.Text = String.Format("Current SATYAM Price: {0}", 
       proxy.GetPrice("SATYAM").ToString());
    }

采取以下步骤创建代理:

步骤 (1) : 在解决方案管理器(SolutionExplorer)的 web 应用入口处右击选择 ‘Add Web Reference’。

步骤 (2) : 选择 ‘Web Services in this solution’,会返回我们编写的股票服务引用。

步骤 (3) : 点击该服务打开测试页面,创建代理时默认为 ‘localhost’,当然你也可以进行重命名。点击 ‘Add Reference’ 来实现向客户端应用程序添加一个代理。

在代码中加入以下语句使之包含该代理:

using localhost;

ASP.NET 多线程

多线程

一个线程被定义为一个程序的执行路径。每个线程都定义了一个独特的流量控制。如果你的应用程序涉及到复杂的和耗时的操作,如数据库访问或一些激烈的 I/O 操作,那么往往设置不同的执行路径或线程,每个线程执行一个特定的工作是非常有益的。

线程是轻量级的进程。使用线程的一个常见的例子是现代操作系统并行编程的的实现。使用线程节省了 CPU 周期的损失,提高了应用效率。

到目前为止我们编译好的程序在一个线程作为一个单一的过程运行,即是应用程序的运行实例。然而,这样的应用程序只可以在某一时刻执行一个工作。让它在同一时间执行多个任务,可以把它分成更小的线程。

在 .Net ,线程是通过 ‘System.Threading’ 的命名空间处理的。创造的 system.threading.thread 类型的变量允许你创建一个新线程开始工作。它允许你在一个单独的线程创建和访问独立的线程。

创建线程

一个线程是由一个线程对象创建的,并给出了它的构造函数的开启线程的参考。

    ThreadStart childthreat = new ThreadStart(childthreadcall);

线程生命周期

一个线程的生命周期开始于 system.threading.thread 类的一个对象被创建,结束于线程被终止或执行完成。

以下是在一个线程的生命周期的各种状态:

  • 待开启状态:该线程的实例被创建但启动方法还未被调用的情况。
  • 就绪状态: 当线程准备好执行并且在等待 CPU 周期的情况。
  • 不可运行状态:线程不能被运行的情况,有以下几种可能:
    • 当前睡眠的方法被调用
    • 等待的方法被调用
    • 被 I/O 操作阻塞
  • 死亡状态:线程执行完毕或已中止的情况。

线程优先权

Thread 类中的优先级属性主要是相对于其他线程指定一个线程的优先级。 .NET 运行时选择具有最高优先级的就绪线程。优先权可分为:

  • 高于正常
  • 低于正常
  • 最高
  • 最低
  • 正常

一旦一个线程被创建,系统就会使用 Thread 类的优先级设置系统设定好它的优先级。

    NewThread.Priority = ThreadPriority.Highest;

线程的属性和方法

线程类具有以下重要特性:

属性 描述
CurrentContext 获取当前正在执行的线程的内容。
CurrentCulture 获取或设置当前线程的环境。
CurrentPrinciple 获取或设置当前进程关于基于角色的安全机制的原则。
CurrentThread 获取当前正在运行的线程。
CurrentUICulture 获取或设置当前运行的进程的资源管理器用于查找特定资源的当前环境。
ExecutionContext 获取包含有关当前线程的上下文信息的 ExecutionContext 对象。
IsAlive 获取一个值,指示当前线程的执行状态。
IsBackground 后台获取或设置一个值指示线程是否是后台线程。
IsThreadPoolThread 获取一个值,指示线程是否属于托管线程池。
ManagedThreadId 获取托管线程的当前唯一标识符。
Name 获取或设置线程的名称。
Priority 获取或设置一个值,表示一个线程的调度优先级。
ThreadState 获取一个值,包含当前线程的状态。

线程类具有以下重要方法:

方法 描述
Abort 调用一个 ThreadAbortException 开始终止线程的过程,调用此方法通常会终止线程。
AllocateDataSlot 向所有线程分配一个未命名的数据槽。为了获得更好的性能,使用标有 ThreadStaticAttribute 属性的域。
AllocateNamedDataSlot 向所有线程上分配已命名的数据槽。为了获得更好的性能,使用的是标有 ThreadStaticAttribute 属性的域。
BeginCriticalRegion 通知宿主执行即将进入代码区域,那里线程中止或未处理的异常的影响可能危及其他任务的应用领域。
BeginThreadAffinity 通知主机托管代码将要执行,取决于当前的物理操作系统线程的标识说明。
EndCriticalRegion 通知宿主执行即将进入代码区域,那里线程中止或未处理的异常仅影响当前任务。
EndThreadAffinity 通知宿主托管代码执行完成,取决于当前的物理操作系统线程的标识说明。
FreeNamedDataSlot 为进程中的所有线程消除名称与槽之间的关联,为了获得更好的性能,使用的是标有 ThreadStaticAttribute 属性的域。
GetData 在当前线程的当前域从当前线程指定的插槽检索值。为了获得更好的性能,使用的是标有 ThreadStaticAttribute 属性的域。
GetDomain 返回当前域中当前正在执行的线程。
GetDomainID 返回唯一的应用程序域标识符。
GetNamedDataSlot 查找已命名的数据槽。为了获得更好的性能,使用的是标有 ThreadStaticAttribute 属性的域。
Interrupt 中断一个在 WaitSleepJoin 线程状态的线程。
Join 阻塞调用线程,直到某个线程终止,同时继续执行标准的 COM 和 SendMessage 。该方法具有不同的重载形式。
MemoryBarrier 同步内存访问如下:处理当前线程的加速器不能以存储器访问调用 MemoryBarrier 后先调用内存访问执行这种方式对指令重新排序。
ResetAbort 取消当前线程的中止请求。
SetData 设置数据在指定的时隙上当前运行的线程,该线程的当前域。为了获得更好的性能,应用领域有 ThreadStaticAttribute 属性的域。
Start 启动一个线程。
Sleep 使线程暂停一个时间段。
SpinWait 使线程等待的参数定义的迭代次数。
VolatileRead() 读取字段的值。最新的值是由计算机的任何处理器写入,不论处理器或处理器缓存的状态数。该方法具有不同的重载形式。
VolatileWrite() 立即向字段写入一个值,这样的值是对计算机中的所有处理器可见。该方法具有不同的重载形式。
Yield 使调用线程执行可以在当前的处理器运行的另一个线程,操作系统选用转向的县城

例子

下面的例子阐明了对线程类的使用。该页面有一个控制标签显示子线程传来的消息。从主程序传来的消息直接使用 response.write(50) 的方法显示出来,因此它们出现在页面的顶部。

源文件如下:

    <%@ Page Language="C#" AutoEventWireup="true" CodeBehind="Default.aspx.cs" Inherits="threaddemo._Default" %>

    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

    <html xmlns="http://www.w3.org/1999/xhtml" >

       <head runat="server">
           <title>
               Untitled Page
           </title>
       </head>

       <body>
           <form id="form1" runat="server">
              <div>
                 <h3>Thread Example</h3>
              </div>

              <asp:Label ID="lblmessage" runat="server" Text="Label">
              </asp:Label>
           </form>
       </body>

    </html>

后台代码如下:

    using System;
    using System.Collections;
    using System.Configuration;
    using System.Data;
    using System.Linq;

    using System.Web;
    using System.Web.Security;
    using System.Web.UI;
    using System.Web.UI.HtmlControls;
    using System.Web.UI.WebControls;
    using System.Web.UI.WebControls.WebParts;

    using System.Xml.Linq;
    using System.Threading;

    namespace threaddemo
    {
       public partial class _Default : System.Web.UI.Page
       {
           protected void Page_Load(object sender, EventArgs e)
           {
               ThreadStart childthreat = new ThreadStart(childthreadcall);
               Response.Write("Child Thread Started <br/>");
               Thread child = new Thread(childthreat);

               child.Start();

               Response.Write("Main sleeping  for 2 seconds.......<br/>");
               Thread.Sleep(2000);
               Response.Write("<br/>Main aborting child thread<br/>");

               child.Abort();
           }

           public void childthreadcall()
           {
               try{
                   lblmessage.Text = "<br />Child thread started <br/>";
                   lblmessage.Text += "Child Thread: Coiunting to 10";

                   for( int i =0; i<10; i++)
                   {
                        Thread.Sleep(500);
                        lblmessage.Text += "<br/> in Child thread </br>";
                   }

                   lblmessage.Text += "<br/> child thread finished";

              }catch(ThreadAbortException e){

              lblmessage.Text += "<br /> child thread - exception";

           }finally{
              lblmessage.Text += "<br /> child thread - unable to catch the  exception";
            }
          }
       }
    }

观察以下:

  • 当页面被加载时,一个新的线程会以 childthreadcall() 为参考开始启动。主线程的活动会直接显示在网页。
  • 第二个线程运行并将消息发送到控制标签。
  • 在子线程执行时主线程休眠 2000 毫秒。
  • 子线程持续运行直到它被主线程中止,然后它会抛出 ThreadAbortException 异常并被终止。
  • 控制返回到主线程。
  • 当执行时程序会发送以下信息:

ASP.NET 配置

配置

一个 ASP.NET 应用程序的行为是由以下两个配置文件中的不同设置决定的:

  • machine.config
  • web.config

machine.config 文件包含所有支持设置项的默认和设置机器的具体值。机器的设置是由系统管理员,且应用程序通常不能访问这个文件。

然而,一个应用程序,可以通过在它的根文件夹中创建 web.config 文件覆盖默认值。web.config 文件是 machine.config 文件的一个子集。

如果应用程序包含子目录,那么它可以为每个文件夹定义一个 web.config 文件。每个配置文件的范围是用一个分层的自上而下的方式确定。

任何 web.config 文件都可以在本地扩展,限制,或重写任何设置在上层的定义。

Visual Studio 会为每个项目生成默认的 web.config 文件。应用程序可以在没有 web.config 文件的情况下执行,然而,我们不能调试一个没有 web.config 文件的应用程序。

下图显示的是用于 web 服务教程中的解决方案资源管理器为样本的例子:

在这种应用中,存在两个 web.config 文件分别对应于调用 web 服务的 web 服务和 web 站点。

web.config 文件中的配置元素是作为根节点的。此元素中的信息分为两个主要领域:配置节处理程序声明区域,和配置节设置区域。

下面的代码片段显示了一个配置文件的基本语法:

    <configuration>

       <!-- Configuration section-handler declaration area. -->
          <configSections>
             <section name="section1" type="section1Handler" />
             <section name="section2" type="section2Handler" />
          </configSections>
       <!-- Configuration section settings area. -->

       <section1>
          <s1Setting1 attribute1="attr1" />
       </section1>

       <section2>
          <s2Setting1 attribute1="attr1" />
       </section2>

       <system.web>
          <authentication mode="Windows" />
       </system.web>

    </configuration>

Configuration Section Handler 声明

配置节处理程序声明是包含在 的标签中的,每个配置处理程序指定配置节的名称,并包含在提供了一些配置数据的文件中。它具有以下基本语法:

    <configSections>
       <section />
       <sectionGroup />
       <remove />
       <clear/>
    </configSections>

它具有以下元素:

  • Clear - 所有涉及继承的节和节组的引用。
  • Remove - 删除一个继承引用的部分和部分组。
  • Section - 定义了配置节处理程序和配置元素之间的关联。
  • Section group - 它定义了一个配置节处理程序与配置节之间的关联。

应用程序设置

应用程序设置允许存储只读访问的应用程序的名称-数值对。例如,你可以定义一个自定义应用程序设置如下:

    <configuration>
       <appSettings>
          <add key="Application Name" value="MyApplication" /> 
       </appSettings>
    </configuration>

例如,你还可存储一本书的 ISBN 号和名字数据对:

    <configuration>
       <appSettings>
          <add key="appISBN" value="0-273-68726-3" />
          <add key="appBook" value="Corporate Finance" />
       </appSettings>
    </configuration>

连接字符串

连接字符串展示的是可用于网站的数据库连接字符串。例如:

    <connectionStrings>
       <add name="ASPDotNetStepByStepConnectionString" 
          connectionString="Provider=Microsoft.Jet.OLEDB.4.0;
          Data Source=E:\\projects\datacaching\ /
          datacaching\App_Data\ASPDotNetStepByStep.mdb"
          providerName="System.Data.OleDb" />

       <add name="booksConnectionString" 
          connectionString="Provider=Microsoft.Jet.OLEDB.4.0;
          Data Source=C:\ \databinding\App_Data\books.mdb"
          providerName="System.Data.OleDb" />
    </connectionStrings>

系统的网络元素

system.web 元素为 ASP.NET 配置节指定了根元素,并且包含了配置 ASP.NET Web 应用程序和控制应用程序运转的配置元素。

它控制大多数比较常见的需要调整的配置元素。该元素的基本语法如下:

    <system.web> 
       <anonymousIdentification> 
       <authentication> 
       <authorization> 
       <browserCaps> 
       <caching> 
       <clientTarget> 
       <compilation> 
       <customErrors> 
       <deployment> 
       <deviceFilters> 
       <globalization> 
       <healthMonitoring> 
       <hostingEnvironment> 
       <httpCookies> 
       <httpHandlers> 
       <httpModules> 
       <httpRuntime> 
       <identity> 
       <machineKey> 
       <membership> 
       <mobileControls> 
       <pages> 
       <processModel> 
       <profile> 
       <roleManager> 
       <securityPolicy> 
       <sessionPageState> 
       <sessionState> 
       <siteMap> 
       <trace> 
       <trust> 
       <urlMappings> 
       <webControls> 
       <webParts> 
       <webServices> 
       <xhtmlConformance> 
    </system.web>

下表提供了一些常用的 system.web 元素的子元素的简要描述:

AnonymousIdentification

这是在需要用户身份确认时对未被认证的用户进行识别的。

Authentication

它是配置授权支持的,基本的语法是:

    <authorization> 
       <allow .../>
       <deny .../>
    </authorization>

Caching

它配置缓存设置,基本的语法是:

    <caching>
       <cache>...</cache>
       <outputCache>...</outputCache>
       <outputCacheSettings>...</outputCacheSettings>
       <sqlCacheDependency>...</sqlCacheDependency>
    </caching>

CustomErrors

它定义了自定义错误消息,基本的语法是:

    <customErrors defaultRedirect="url" mode="On|Off|RemoteOnly">
       <error. . ./>
    </customErrors>

Deployment

它定义了用于部署的配置设置。基本语法如下:

    <deployment retail="true|false" />

HostingEnvironment

它为托管环境定义了配置设置。基本语法如下:

    <hostingEnvironment idleTimeout="HH:MM:SS" shadowCopyBinAssemblies="true|false" 
       shutdownTimeout="number" urlMetadataSlidingExpiration="HH:MM:SS" />

Identity

它用于配置对应用程序的认证机制,基本语法如下:

    <identity impersonate="true|false" userName="domain\username"
       password="<secure password>"/>

MachineKey

它用于配置用于加密和解密数据的表单验证 Cookie 的密钥。

它还允许配置验证密钥对视图状态数据和 Forms 身份验证票证执行消息认证检查。基本的语法是:

    <machineKey validationKey="AutoGenerate,IsolateApps" [String]
       decryptionKey="AutoGenerate,IsolateApps" [String]
       validation="HMACSHA256" [SHA1 | MD5 | 3DES | AES | HMACSHA256 | 
       HMACSHA384 | HMACSHA512 | alg:algorithm_name]
       decryption="Auto" [Auto | DES | 3DES | AES | alg:algorithm_name]
    />

Membership

它用于配置管理和认证用户参数。基本的语法是:

    <membership defaultProvider="provider name"
       userIsOnlineTimeWindow="number of minutes" hashAlgorithmType="SHA1">
       <providers>...</providers>
    </membership>

Pages

它提供了网页的具体配置。基本的语法是:

    <pages asyncTimeout="number" autoEventWireup="[True|False]"
         buffer="[True|False]" clientIDMode="[AutoID|Predictable|Static]"
         compilationMode="[Always|Auto|Never]" 
         controlRenderingCompatibilityVersion="[3.5|4.0]"
         enableEventValidation="[True|False]"
         enableSessionState="[True|False|ReadOnly]"
         enableViewState="[True|False]"
         enableViewStateMac="[True|False]"
         maintainScrollPositionOnPostBack="[True|False]" 
         masterPageFile="file path" 
         maxPageStateFieldLength="number" 
         pageBaseType="typename, assembly"
         pageParserFilterType="string" 
         smartNavigation="[True|False]"
         styleSheetTheme="string"
         theme="string"
         userControlBaseType="typename"
         validateRequest="[True|False]"
         viewStateEncryptionMode="[Always|Auto|Never]" >

       <controls>...</controls>
       <namespaces>...</namespaces>
       <tagMapping>...</tagMapping>
       <ignoreDeviceFilters>...</ignoreDeviceFilters>
    </pages>

Profile

它用于配置用户配置文件参数。基本的语法是:

    <profile enabled="true|false" inherits="fully qualified type reference"
       automaticSaveEnabled="true|false" defaultProvider="provider name">

       <properties>...</properties>
       <providers>...</providers>

    </profile>

RoleManager

为用户角色配置设置信息。基本的语法是:

    <roleManager cacheRolesInCookie="true|false" cookieName="name"
       cookiePath="/" cookieProtection="All|Encryption|Validation|None"
       cookieRequireSSL="true|false " cookieSlidingExpiration="true|false "
       cookieTimeout="number of minutes" createPersistentCookie="true|false"
       defaultProvider="provider name" domain="cookie domain"> 
       enabled="true|false"
       maxCachedResults="maximum number of role names cached"

       <providers>...</providers>
    </roleManager>

SecurityPolicy

用于配置安全策略。基本的语法是:

    <securityPolicy>
       <trustLevel />
    </securityPolicy>

UrlMappings

它定义了用于隐藏原始URL的映射,并提供更具用户友好性的的 URL 。基本的语法是:

    <urlMappings enabled="true|false">
       <add.../>
       <clear />
       <remove.../>
    </urlMappings>

WebControls

它提供了对客户端脚本共享位置的名称。基本的语法是:

    <webControls clientScriptsLocation="String" />

WebServices

用于配置 Web 服务。

ASP.NET 部署

部署

目前存在两类 ASP.NET 部署:

  • Local deployment(本地部署):在这种情况下,整个应用程序都包含在一个虚拟目录下,所有的内容和程序集都包含在其中,可被应用程序使用。
  • Global deployment(全局部署):在这种情况下,组件可以被每一个在应用服务器上运行的应用程序所使用。

然而在部署中我们可以利用多种不同的技术,以下我们将讨论最常见和最简单的部署方式:

  • XCOPY 部署
  • 复制一个网站
  • 创建一个设置项目

XCOPY 部署

XCOPY 部署是说将所有文件递归拷贝到目标计算机上的目标文件夹。你可以使用以下任何的常用技术:

  • FTP 传输
  • 使用提供对远程站点复制的服务器管理工具
  • MSI 安装程序应用

XCOPY 部署简单地拷贝程序文件到生产服务器,并在其中设置一个虚拟目录。你需要使用互联网信息微软管理控制台(MMC 管理单元)去设置虚拟目录。

复制一个网站

在 Visual Studio 中复制网站是可用的选项。它可在 Website -> Copy Web Site 菜单选项中实现。此菜单项允许复制当前网站到另一个本地或远程位置。它是一种集成的 FTP 工具。

使用以下几种选项连接到你的目的地来选择所需的复制模式:

  • 覆盖
  • 从源到目标文件
  • 同步源和目标项目

然后通过物理方式复制文件。这里不像 XCOPY 部署,这个过程是从 Visual Studio 环境中进行部署的。然而,以下两个以上部署的方法都有以下问题:

  • 你传递了你的源代码。
  • 没有预编译和错误检查的文件。
  • 初始页面加载会很慢。

创建一个设置项目

在这种方法中,你使用了 Windows Installer 并且打包好 Web 应用程序使它可以部署在生产服务器。Visual Studio 允许你创建部署包。那么让我们测试一个我们现有的项目,数据绑定方案。

打开项目,采取以下步骤:

步骤(1):Select File -> Add -> New Project,使用显示在解决方案资源管理器的网站根目录。

步骤(2):在 Other Project Types 下选择 Setup and Deployment,然后选择 Setup Wizard。

步骤(3):选择默认位置,确保将在根目录下自己的文件夹站点下建立项目。点 Okay 我们就得到了向导的第一个屏幕。

步骤(4):选择项目类型。选择 ‘Create a setup for a web application’。

步骤(5):下一步,第三屏要从解决方案中的所有项目选择项目输出。选中复选框旁边的 ‘Content Files from...’

步骤(6):第四屏幕允许包括例如自述等其他文件。然而,在我们的案例中并没有这样的文件。单击结束。

步骤(7):最后的屏幕显示设置项目总结。

步骤(8):设置项目添加到解决方案资源管理器,主设计窗口中会显示一个文件系统编辑器。

步骤(9):下一步是创建安装项目。在 Solution Explorer 中右键单击项目名称,选择 Build。

步骤(10):当建立完成后,你在输出窗口中会得到以下信息:

两个文件在生成过程被创建:

  • Setup.exe
  • Setup-databinding.msi

你需要将这些文件复制到服务器。在本地机器上双击安装文件来安装 MSI 文件的内容。

未完待续,下一章节,つづく

猜你喜欢

转载自blog.csdn.net/u012576807/article/details/81432973