ASP.NET 4.5 中 Web 窗体的新增功能

Web Camp 团队

新版本的 ASP.NET Web Forms 引入了许多改进,这些改进侧重于在处理数据时改善用户体验。

在早期版本的 Web Forms 中,使用数据绑定发出对象成员的值时,使用了数据绑定表达式 Bind () 或 Eval () 。 在新版本的 ASP.NET 中,可以使用新的 ItemType 属性声明控件要绑定到的数据类型。 通过设置此属性,可以使用强类型变量来获得 Visual Studio 开发体验的全部优势,例如 IntelliSense、成员导航和编译时检查。

使用数据绑定控件,现在还可以指定自己的自定义方法来选择、更新、删除和插入数据,从而简化页面控件与应用程序逻辑之间的交互。 此外,模型绑定功能已添加到 ASP.NET,这意味着可以将页面中的数据直接映射到方法类型参数。

使用最新版本的 Web Forms,验证用户输入应该也更容易。 现在,可以使用 System.ComponentModel.DataAnnotations 命名空间中的验证属性对模型类进行批注,并请求所有站点控件使用该信息验证用户输入。 Web Forms 中的客户端验证现已与 jQuery 集成,提供更简洁的客户端代码和不显眼的 JavaScript 功能。

在请求验证区域中,进行了改进,可以更轻松地有选择地关闭应用程序特定部分的请求验证或读取无效的请求数据。

对Web Forms服务器控件进行了一些改进,以利用 HTML5 的新功能:

  • TextBox 控件的 TextMode 属性已更新为支持新的 HTML5 输入类型,如电子邮件、日期时间等。
  • FileUpload 控件现在支持从支持此 HTML5 功能的浏览器上传多个文件。
  • 验证程序控件现在支持验证 HTML5 输入元素。
  • 具有表示 URL 的属性的新 HTML5 元素现在支持 runat=“server”。 因此,可以使用 URL 路径中的 ASP.NET 约定(如 ~ 运算符)来表示应用程序根 (例如 video <runat=“server” src=“~/myVideo.wmv”></video>) 。
  • UpdatePanel 控件已修复为支持发布 HTML5 输入字段。

在官方 ASP.NET 门户中,可以在 ASP.NET WebForms 4.5:ASP.NET 4.5 和 Visual Studio 2012 中的新增功能中找到更多新功能示例

所有示例代码和代码片段都包含在 Web Camp 培训工具包中。

目标

在本动手实验中,您将了解如何:

  • 使用强类型数据绑定表达式
  • 在 Web Forms 中使用新的模型绑定功能
  • 使用值提供程序将页面数据映射到代码隐藏方法
  • 使用数据注释进行用户输入验证
  • 在 Web Forms 中利用 jQuery 的不显眼的客户端验证
  • 实现精细请求验证
  • 在 Web Forms 中实现异步页面处理

先决条件

必须具有以下项才能完成此实验室:

设置

安装代码片段

为方便起见,你将在本实验室中管理的大部分代码都以 Visual Studio 代码片段的形式提供。 若要安装代码片段,请运行 .\Source\Setup\CodeSnippets.vsi 文件。

如果不熟悉Visual Studio Code代码段,并想要了解如何使用它们,可以参考本文档“附录 C:使用代码片段”中的附录。

练习

本动手实验包括以下练习:

  1. 练习 1:ASP.NET Web Forms 中的模型绑定
  2. 练习 2:数据验证
  3. 练习 3:ASP.NET Web Forms中的异步页面处理

注意

每个练习都附带一个 End 文件夹,其中包含在完成练习后应获得的结果解决方案。 如果需要其他帮助完成练习,可以使用此解决方案作为指导。

完成本实验室的估计时间: 60 分钟

练习 1:ASP.NET Web Forms 中的模型绑定

新版本的 ASP.NET Web Forms 引入了许多增强功能,这些增强功能侧重于改进处理数据时的体验。 在本练习中,你将了解强类型数据控件和模型绑定。

任务 1 - 使用 Strongly-Typed Data-Bindings

在此任务中,你将发现 ASP.NET 4.5 中可用的新强类型绑定。

  1. 打开 Source/Ex1-ModelBinding/Begin/ 文件夹中的 Begin 解决方案。

    1. 在继续之前,需要下载一些缺少的 NuGet 包。 为此,请单击“ 项目 ”菜单,然后选择“ 管理 NuGet 包”。

    2. “管理 NuGet 包 ”对话框中,单击“ 还原 ”以下载缺少的包。

    3. 最后,单击“生成 | 解决方案”生成解决方案

      注意

      使用 NuGet 的优点之一是,无需交付项目中的所有库,从而减小了项目大小。 使用 NuGet Power Tools,通过在 Packages.config 文件中指定包版本,你将能够在首次运行项目时下载所有必需的库。 这就是在从本实验室打开现有解决方案后必须运行这些步骤的原因。

  2. 打开 Customers.aspx 页。 在 main 控件中放置一个未编号的列表,并在 其中包含一个中继器控件,用于列出每个客户。 将中继器名称设置为 customersRepeater ,如以下代码所示。

    在以前版本的 Web Forms 中,使用数据绑定发出要绑定到的对象上的成员的值时,将使用数据绑定表达式和对 Eval 方法的调用,将成员的名称作为字符串传递。

    在运行时,这些对 Eval 的调用将针对当前绑定的对象使用反射来读取具有给定名称的成员的值,并在 HTML 中显示结果。 此方法使针对任意、未调整的数据进行数据绑定变得非常简单。

    遗憾的是,你失去了 Visual Studio 中许多出色的开发时体验功能,包括成员名称的 IntelliSense、对导航 ((如转到定义) )的支持,以及编译时检查。

    ...
    <asp:Content ID="Content3" ContentPlaceHolderID="MainContent" runat="server">
      <h3>Customers</h3>
      <ul>
        <asp:Repeater ID="customersRepeater" runat="server">
          <ItemTemplate>
                <li>
                    <%# Eval("FirstName") %>
                    <%# Eval("LastName") %>
                </li>
          </ItemTemplate>
        </asp:Repeater>
      </ul>
      <a href="CustomerDetails.aspx">Add a New Customer</a>
    </asp:Content>
    
  3. 打开 Customers.aspx.cs 文件。

  4. 添加以下 using 语句。

    using System.Linq;
    
  5. Page_Load 方法中,添加代码以使用客户列表填充中继器。

    (代码片段 - Web Forms实验室 - Ex01 - 绑定客户数据源)

    protected void Page_Load(object sender, EventArgs e)
    {
        using (var db = new WebFormsLab.Model.ProductsContext())
        {
            this.customersRepeater.DataSource = db.Customers.ToList();
            this.customersRepeater.DataBind();
        }
    }
    

    该解决方案使用 EntityFramework 和 CodeFirst 来创建和访问数据库。 在以下代码中,customersRepeater 绑定到具体化查询,该查询从数据库返回所有客户。

  6. F5 运行解决方案,并转到 “客户 ”页面以查看中继器在操作中。 由于解决方案使用的是 CodeFirst,因此运行应用程序时,将在本地 SQL Express 实例中创建并填充数据库。

    使用中继器列出客户

    使用中继器列出客户

    注意

    在 Visual Studio 2012 中,IIS Express 是默认的 Web 开发服务器。

  7. 关闭浏览器并返回到 Visual Studio。

  8. 现在,替换 实现以使用强类型绑定。 打开 Customers.aspx 页,并使用中继器中的新 ItemType 属性将 Customer 类型设置为绑定类型。

    <asp:Content ID="Content2" ContentPlaceHolderID="MainContent" runat="server">
      <ul>
        <asp:Repeater ID="customersRepeater" 
                      ItemType="WebFormsLab.Model.Customer" 
                      runat="server">
          <ItemTemplate>
             ...
          </ItemTemplate>
        </asp:Repeater>
      </ul>
      <a href="CustomerDetails.aspx">Add a New Customer</a>
    </asp:Content>
    

    使用 ItemType 属性可以声明控件要绑定到的数据类型,并允许在数据绑定控件中使用强类型绑定。

  9. 将 ItemTemplate 内容替换为以下代码。

    <asp:Content ID="Content2" ContentPlaceHolderID="MainContent" runat="server">
      ...
      <ul>
        <asp:Repeater ID="customersRepeater" ItemType="WebFormsLab.Model.Customer" runat="server">
          <ItemTemplate>
            <li>
              <a href="CustomerDetails.aspx?id=<%#: Item.Id %>">
                <%#: Item.FirstName %> <%#: Item.LastName %>
              </a>
            </li>
          </ItemTemplate>
        </asp:Repeater>
      </ul>
      <a href="CustomerDetails.aspx">Add a New Customer</a>
    </asp:Content>
    

    上述方法的一个缺点是,对 Eval () 和 Bind () 的调用是后期绑定的 , 这意味着传递字符串来表示属性名称。 这意味着你不会获得成员名称的 Intellisense、对代码导航 ((如转到定义) )的支持,也没有编译时检查支持。

    设置 ItemType 属性会导致在数据绑定表达式的范围内生成两个新的类型化变量: ItemBindItem。 可以在数据绑定表达式中使用这些强类型变量,并获取 Visual Studio 开发体验的全部优势。

    表达式中使用的“ ”会自动对输出进行 HTML 编码,以避免安全问题 (例如跨站点脚本攻击) 。 此表示法自 .NET 4 起可用于响应写入,但现在也可用于数据绑定表达式。

    注意

    Item 成员适用于单向绑定。 如果要执行双向绑定,请使用 BindItem 成员。

    强类型绑定中的 IntelliSense 支持强类型绑定

    强类型绑定中的 IntelliSense 支持

  10. F5 运行解决方案,并转到“客户”页,确保更改按预期方式工作。

    列出客户详细信息

    列出客户详细信息

  11. 关闭浏览器并返回到 Visual Studio。

任务 2 - 在 Web Forms 中介绍模型绑定

在早期版本的 ASP.NET Web Forms 中,当想要执行双向数据绑定(同时检索和更新数据)时,需要使用数据源对象。 这可以是对象数据源、SQL 数据源、LINQ 数据源等。 但是,如果你的方案需要用于处理数据的自定义代码,则需要使用对象数据源,这带来了一些缺点。 例如,需要避免复杂类型,在执行验证逻辑时需要处理异常。

在新版本 ASP.NET Web Forms数据绑定控件支持模型绑定。 这意味着可以直接在数据绑定控件中指定选择、更新、插入和删除方法,以从代码隐藏文件或其他类调用逻辑。

若要了解这一点,你将使用 GridView 使用新的 SelectMethod 属性列出产品类别。 使用此属性可以指定用于检索 GridView 数据的方法。

  1. 打开 Products.aspx 页并包含 GridView。 如下所示配置 GridView 以使用强类型绑定并启用排序和分页。

    <asp:Content ID="Content3" ContentPlaceHolderID="MainContent" runat="server">
     <asp:GridView ID="categoriesGrid" runat="server"
        AutoGenerateColumns="false"
        ItemType="WebFormsLab.Model.Category" DataKeyNames="CategoryID">
        <Columns>
          <asp:BoundField DataField="CategoryId" HeaderText="ID" SortExpression="CategoryId" />
          <asp:BoundField DataField="CategoryName" HeaderText="Name" SortExpression="CategoryName" />
          <asp:BoundField DataField="Description" HeaderText="Description" />
          <asp:TemplateField HeaderText="# of Products">
            <ItemTemplate><%#: Item.Products.Count %></ItemTemplate>
          </asp:TemplateField>
        </Columns>
      </asp:GridView>
    </asp:Content>
    
  2. 使用新的 SelectMethod 属性将 GridView 配置为调用 GetCategories 方法以选择数据。

    <asp:GridView ID="categoriesGrid" runat="server"
        AutoGenerateColumns="false"
        ItemType="WebFormsLab.Model.Category" DataKeyNames="CategoryId"
        SelectMethod="GetCategories">
      <Columns>
        <asp:BoundField DataField="CategoryId" HeaderText="ID" SortExpression="CategoryId" />
        <asp:BoundField DataField="CategoryName" HeaderText="Name" SortExpression="CategoryName" />
        <asp:BoundField DataField="Description" HeaderText="Description" />
        <asp:TemplateField HeaderText="# of Products">
          <ItemTemplate><%#: Item.Products.Count %></ItemTemplate>
        </asp:TemplateField>
      </Columns>
    </asp:GridView>
    
  3. 打开 Products.aspx.cs 代码隐藏文件并添加以下 using 语句。

    (代码片段 - Web Forms实验室 - Ex01 - 命名空间)

    using System.Collections.Generic;
    using System.Data.Entity;
    using System.Data.Entity.Infrastructure;
    using System.Linq;
    using WebFormsLab.Model;
    
  4. Products 类中添加专用成员,并分配 ProductsContext 的新实例。 此属性将存储用于连接到数据库的 Entity Framework 数据上下文。

    public partial class Products : System.Web.UI.Page
    {
        private ProductsContext db = new ProductsContext();
        ...
    
  5. 创建 GetCategories 方法以使用 LINQ 检索类别列表。 查询将包含 Products 属性,因此 GridView 可以显示每个类别的产品数量。 请注意, 方法返回一个原始 IQueryable 对象,该对象表示稍后将在页面生命周期中执行的查询。

    (代码片段 - Web Forms实验室 - Ex01 - GetCategories)

    public IQueryable<Category> GetCategories()
    {
      var query = this.db.Categories
        .Include(c => c.Products);
    
      return query;
    }
    

    注意

    在早期版本的 ASP.NET Web Forms中,使用自己的存储库逻辑在对象数据源上下文中启用排序和分页,需要编写自己的自定义代码并接收所有必要的参数。 现在,由于数据绑定方法可以返回 IQueryable,这表示仍要执行的查询,ASP.NET 可以负责修改查询以添加正确的排序和分页参数。

  6. F5 开始调试站点,并转到“产品”页。 应会看到 GridView 填充了 GetCategories 方法返回的类别。

    使用模型绑定填充 GridView

    使用模型绑定填充 GridView

  7. SHIFT+F5 停止调试。

任务 3 - 模型绑定中的值提供程序

模型绑定不仅使你能够指定自定义方法以直接在数据绑定控件中处理数据,还允许你将页面中的数据映射到这些方法中的参数。 在 方法参数上,可以使用值提供程序属性指定值的数据源。 例如:

  • 页面上的控件
  • 查询字符串值
  • 查看数据
  • 会话状态
  • Cookie
  • 已发布的表单数据
  • 查看状态
  • 还支持自定义值提供程序

如果使用了 ASP.NET MVC 4,则会注意到模型绑定支持是类似的。 事实上,这些功能取自 ASP.NET MVC,并移动到 System.Web 程序集中,以便能够在Web Forms上使用它们。

在此任务中,你将更新 GridView 以按每个类别的产品量筛选其结果,并接收带有模型绑定的筛选器参数。

  1. 返回到 Products.aspx 页。

  2. 在 GridView 顶部,添加 标签ComboBox 以选择每个类别的产品数,如下所示。

    <h3>Categories</h3>
    <asp:Label ID="Label1" runat="server" AssociatedControlID="minProductsCount">
         Show categories with at least this number of products:
    </asp:Label>
    <asp:DropDownList runat="server" ID="minProductsCount" AutoPostBack="true">
      <asp:ListItem Value="" Text="-" />
      <asp:ListItem Text="1" />
      <asp:ListItem Text="3" />
      <asp:ListItem Text="5" />
    </asp:DropDownList>
    <br/>
    
  3. EmptyDataTemplate 添加到 GridView,以在没有具有所选产品数量的类别时显示一条消息。

    <asp:GridView ID="categoriesGrid" runat="server"
        AutoGenerateColumns="false"
        ItemType="WebFormsLab.Model.Category" DataKeyNames="CategoryId"
        SelectMethod="GetCategories">
      <Columns>
        <asp:BoundField DataField="CategoryId" HeaderText="ID" />
        <asp:BoundField DataField="CategoryName" HeaderText="Name" />
        <asp:BoundField DataField="Description" HeaderText="Description" />
        <asp:TemplateField HeaderText="# of Products">
          <ItemTemplate><%#: Item.Products.Count %></ItemTemplate>
        </asp:TemplateField>
      </Columns>
      <EmptyDataTemplate>
          No categories found with a product count of <%#: minProductsCount.SelectedValue %>
      </EmptyDataTemplate>
    </asp:GridView>
    
  4. 打开 Products.aspx.cs 代码隐藏并添加以下 using 语句。

    using System.Web.ModelBinding;
    
  5. 修改 GetCategories 方法以接收整数 minProductsCount 参数并筛选返回的结果。 为此,请将 方法替换为以下代码。

    (代码片段 - Web Forms实验室 - Ex01 - GetCategories 2)

    public IQueryable<Category> GetCategories([Control]int? minProductsCount)
    {
        var query = this.db.Categories
        .Include(c => c.Products);
    
        if (minProductsCount.HasValue)
        {
            query = query.Where(c => c.Products.Count >= minProductsCount);
        }
    
        return query;
    }
    

    minProductsCount 参数上的新 [Control] 属性将使 ASP.NET 知道必须使用页面中的控件填充其值。 ASP.NET 将查找与 minProductsCount () 参数名称匹配的任何控件,并执行必要的映射和转换,以使用控件值填充参数。

    或者,特性提供重载的构造函数,使你可以指定控件从何处获取值。

    注意

    数据绑定功能的一个目标是减少需要为页面交互编写的代码量。 除了 [Control] 值提供程序外,还可以在方法参数中使用其他模型绑定提供程序。 其中一些已在任务简介中列出。

  6. F5 开始调试站点,并转到“产品”页。 在下拉列表中选择许多产品,并注意 GridView 现在是如何更新的。

    使用下拉列表值筛选 GridView

    使用下拉列表值筛选 GridView

  7. 停止调试。

任务 4 - 使用模型绑定进行筛选

在此任务中,你将添加第二个子 GridView,以显示所选类别中的产品。

  1. 打开 Products.aspx 页并更新类别 GridView 以自动生成“选择”按钮。

    <asp:GridView ID="categoriesGrid" runat="server"
      AutoGenerateColumns="false"
      ItemType="WebFormsLab.Model.Category" DataKeyNames="CategoryId"
      SelectMethod="GetCategories"
      AutoGenerateSelectButton="true">
    
  2. 在底部添加名为 productsGrid 的第二个 GridView。 将 ItemType 设置为 WebFormsLab.Model.Product,将 DataKeyNames 设置为 ProductId将 SelectMethod 设置为 GetProducts。 将 AutoGenerateColumns 设置为 false ,并添加 ProductId、ProductName、Description 和 UnitPrice 的列。

    <h3>Products</h3>
    <asp:GridView ID="productsGrid" runat="server" 
        CellPadding="4"
        AutoGenerateColumns="false"
        ItemType="WebFormsLab.Model.Product"
        DataKeyNames="ProductId"
        SelectMethod="GetProducts">
        <Columns>
            <asp:BoundField DataField="ProductId" HeaderText="ID" />
            <asp:BoundField DataField="ProductName" HeaderText="Name" />
            <asp:BoundField DataField="Description" HeaderText="Description" HtmlEncode="false" />
            <asp:BoundField DataField="UnitPrice" HeaderText="Price" />
        </Columns>
        <EmptyDataTemplate>
            Select a category above to see its products
        </EmptyDataTemplate>
    </asp:GridView>
    
  3. 打开 Products.aspx.cs 代码隐藏文件。 实现 GetProducts 方法以从类别 GridView 接收类别 ID 并筛选产品。 模型绑定将使用 categoriesGrid 中的所选行设置参数值。 由于参数名称和控件名称不匹配,因此应在 Control 值提供程序中指定控件的名称。

    (代码片段 - Web Forms实验室 - Ex01 - GetProducts)

    public IEnumerable<Product> GetProducts([Control("categoriesGrid")]int? categoryId)
    {
        return this.db.Products.Where(p => p.CategoryId == categoryId);
    }
    

    注意

    此方法使单元测试这些方法变得更加容易。 在单元测试上下文中,Web Forms未执行,[Control] 属性不会执行任何特定操作。

  4. 打开 Products.aspx 页面并找到产品 GridView。 更新产品 GridView 以显示用于编辑所选产品的链接。

    <h3>Products</h3>
    <asp:GridView ID="productsGrid" runat="server" 
      CellPadding="4"
      AutoGenerateColumns="false"
      ItemType="WebFormsLab.Model.Product"
      DataKeyNames="ProductId"
      SelectMethod="GetProducts">
      <Columns>
        <asp:TemplateField>
          <ItemTemplate>
            <a href="ProductDetails.aspx?productId=<%#: Item.ProductId %>">View</a>
          </ItemTemplate>
        </asp:TemplateField>
        <asp:BoundField DataField="ProductId" HeaderText="ID" />
        <asp:BoundField DataField="ProductName" HeaderText="Name" />
        <asp:BoundField DataField="Description" HeaderText="Description" HtmlEncode="false" />
        <asp:BoundField DataField="UnitPrice" HeaderText="Price" />
      </Columns>
      <EmptyDataTemplate>
        Select a category above to see its products
      </EmptyDataTemplate>
    </asp:GridView>
    
  5. 打开 ProductDetails.aspx 页代码隐藏,并将 SelectProduct 方法替换为以下代码。

    (代码片段 - Web Forms实验室 - Ex01 - SelectProduct 方法)

    public Product SelectProduct([QueryString]int? productId)
    {
        return this.db.Products.Find(productId);
    }
    

    注意

    请注意, [QueryString] 属性用于从查询字符串中的 productId 参数填充方法参数。

  6. F5 开始调试站点,并转到“产品”页。 从类别 GridView 中选择任意类别,并注意到产品 GridView 已更新。

    显示所选类别中的产品

    显示所选类别中的产品

  7. 单击产品上的 “查看” 链接,打开“ProductDetails.aspx”页。

    请注意,页面使用查询字符串中的 productId 参数检索具有 SelectMethod 的产品。

    查看产品详细信息

    查看产品详细信息

    注意

    在下一个练习中将实现键入 HTML 说明的功能。

任务 5 - 使用模型绑定执行更新操作

在上一个任务中,你主要使用模型绑定来选择数据,在本任务中,你将了解如何在更新操作中使用模型绑定。

你将更新类别 GridView 以允许用户更新类别。

  1. 打开 Products.aspx 页并更新类别 GridView 以自动生成“编辑”按钮,并使用新的 UpdateMethod 属性指定 UpdateCategory 方法来更新所选项目。

    <asp:GridView ID="categoriesGrid" runat="server"
        AutoGenerateColumns="false"
        ItemType="WebFormsLab.Model.Category" DataKeyNames="CategoryId"
        SelectMethod="GetCategories"
        AutoGenerateSelectButton="true"
        AutoGenerateEditButton="true"
        UpdateMethod="UpdateCategory">
    

    GridView 中的 DataKeyNames 属性定义哪些是唯一标识模型绑定对象的成员,因此,这些成员是更新方法至少应接收的参数。

  2. 打开 Products.aspx.cs 代码隐藏文件并实现 UpdateCategory 方法。 方法应接收类别 ID 以加载当前类别,填充 GridView 中的值,然后更新类别。

    (代码片段 - Web Forms 实验室 - Ex01 - UpdateCategory)

    public void UpdateCategory(int categoryId)
    {
        var category = this.db.Categories.Find(categoryId);
    
        this.TryUpdateModel(category);
    
        if (this.ModelState.IsValid)
        {
            this.db.SaveChanges();
        }
    }
    

    Page 类中的新 TryUpdateModel 方法负责使用页面中控件中的值填充模型对象。 在这种情况下,它将替换当前 GridView 行中正在编辑到 类别 对象中的更新值。

    注意

    下一个练习将说明 ModelState.IsValid 用于验证用户在编辑对象时输入的数据。

  3. 运行站点并转到“产品”页。 编辑类别。 键入新名称,然后单击“ 更新 ”以保留更改。

    编辑类别

    编辑类别

练习 2:数据验证

在本练习中,你将了解 ASP.NET 4.5 中的新数据验证功能。 你将在 Web Forms 中检查新的非侵入式验证功能。 你将使用应用程序模型类中的数据注释进行用户输入验证,最后,你将了解如何打开或关闭对页面中各个控件的请求验证。

任务 1 - 不显眼的验证

包含复杂数据(包括验证程序)的表单往往在页面中生成过多的 JavaScript 代码,这可能代表大约 60% 的代码。 启用不显眼的验证后,HTML 代码将看起来更简洁、更简洁。

在本部分中,你将在 ASP.NET 中启用不显眼的验证,以比较这两个配置生成的 HTML 代码。

  1. 打开 Visual Studio 2012 并打开位于本实验室的 Source\Ex2-Validation\Begin 文件夹中的 Begin 解决方案。 或者,可以继续处理上一练习中的现有解决方案。

    1. 如果打开了提供的 Begin 解决方案,则需要先下载一些缺少的 NuGet 包,然后才能继续。 为此,请在解决方案资源管理器中,单击“WebFormsLab”项目“”管理 NuGet 包”。

    2. “管理 NuGet 包 ”对话框中,单击“ 还原 ”以下载缺少的包。

    3. 最后,单击“生成 | 解决方案”生成解决方案

      注意

      使用 NuGet 的优点之一是无需交付项目中的所有库,从而减少了项目大小。 使用 NuGet Power Tools,通过在 Packages.config 文件中指定包版本,你将能够在首次运行项目时下载所有必需的库。 这就是在打开本实验室中的现有解决方案后,必须运行这些步骤的原因。

  2. F5 启动 Web 应用程序。 转到“客户”页,然后单击“ 添加新客户” 链接。

  3. 右键单击浏览器页面,然后选择“ 查看源 ”选项以打开应用程序生成的 HTML 代码。

    显示页面 HTML 代码

    显示页面 HTML 代码

  4. 滚动浏览页面源代码,注意 ASP.NET 已在页面中注入 JavaScript 代码和数据验证程序,以执行验证并显示错误列表。

    CustomerDetails 页中的验证 JavaScript 代码

    CustomerDetails 页中的验证 JavaScript 代码

  5. 关闭浏览器并返回到 Visual Studio。

  6. 现在,你将启用不显眼的验证。 打开Web.Config,在 AppSettings 节中找到 ValidationSettings:UnobtrusiveValidationMode将键值设置为 WebForms

    <configuration>
      ...
      <appSettings>
        <add key="aspnet:uselegacysynchronizationcontext" value="false" />
        <add key="ValidationSettings:UnobtrusiveValidationMode" value="WebForms"/>
    

    注意

    还可以在“Page_Load”事件中设置此属性,以便仅对某些页面启用不显眼的验证。

  7. 打开 CustomerDetails.aspx 并按 F5 启动 Web 应用程序。

  8. 按 F12 键打开 IE 开发人员工具。 打开开发人员工具后,选择脚本选项卡。从菜单中选择 CustomerDetails.aspx ,并注意,在页面上运行 jQuery 所需的脚本已从本地站点加载到浏览器中。

    直接从本地 IIS 服务器加载 jQuery JavaScript 文件

    直接从本地 IIS 服务器加载 jQuery JavaScript 文件

  9. 关闭浏览器以返回到 Visual Studio。 再次打开 Site.Master 文件并找到 ScriptManager。 添加值为 True 的属性 EnableCdn 属性。 这将强制从联机 URL 加载 jQuery,而不是从本地站点的 URL 加载。

  10. 在 Visual Studio 中打开 CustomerDetails.aspx 。 按 F5 键运行站点。 Internet Explorer 打开后,按 F12 键打开开发人员工具。 选择“ 脚本 ”选项卡,然后查看下拉列表。 请注意,jQuery JavaScript 文件不再从本地站点加载,而是从联机 jQuery CDN 加载。

    从 CDN 加载 jQuery JavaScript 文件

    从 CDN 加载 jQuery JavaScript 文件

  11. 使用浏览器中的“查看源”选项再次打开 HTML 页源代码。 请注意,通过启用不显眼的验证 ASP.NET 已将注入的 JavaScript 代码替换为 data- *属性。

    不显眼验证代码

    不显眼的验证代码

    注意

    在此示例中,你了解了如何将包含数据注释的验证摘要简化为只有几行 HTML 和 JavaScript 行。 以前,如果不进行不显眼的验证,添加的验证控件越多,JavaScript 验证代码就越大。

任务 2 - 使用数据注释验证模型

ASP.NET 4.5 引入了Web Forms的数据注释验证。 现在可以在模型类中定义约束,并跨所有 Web 应用程序使用这些约束,而不是对每个输入使用验证控件。 在本部分中,你将了解如何使用数据注释来验证新的/编辑客户表单。

  1. 打开 CustomerDetail.aspx 页。 请注意, EditItemTemplateInsertItemTemplate 部分中的客户名字和第二个名称是使用 RequiredFieldValidator 控件验证的。 每个验证程序都与特定条件相关联,因此需要包含与条件一样多的验证程序才能检查。

  2. 添加数据注释以验证 Customer 模型类。 打开 Model 文件夹中的 Customer.cs 类,并使用数据注释属性修饰每个属性。

    (代码片段 - Web Forms实验室 - Ex02 - 数据注释)

    namespace WebFormsLab.Model
    {
      using System.Collections.Generic;
      using System.ComponentModel.DataAnnotations;
    
      public class Customer
      {
         [Key]
         public int Id { get; set; }
    
         [Required]
         public string FirstName { get; set; }
    
         [Required]
         public string LastName { get; set; }
    
         [Range(0, 130)]
         public int Age { get; set; }
    
         public Address Address { get; set; }
    
         [Phone]
         public string DaytimePhone { get; set; }
    
         [EmailAddress, StringLength(256)]
         public string EmailAddress { get; set; }
      }
    }
    

    注意

    .NET Framework 4.5 扩展了现有数据注释集合。 以下是可以使用的一些数据注释:[CreditCard]、[Phone]、[EmailAddress]、[Range]、[Compare]、[Url]、[FileExtensions]、[Required]、[Key]、[RegularExpression]。

    一些用法示例:

    [Key]:指定属性是唯一标识符

    [Range (0.4, 0.5, ErrorMessage=“{Write an error message}”]:双范围

    [EmailAddress (ErrorMessage=“Invalid Email”) , MaxLength (56) ]:同一行中的两个注释。

    还可以在每个属性中定义自己的错误消息。

  3. 打开 CustomerDetails.aspx ,并删除 FormView 控件的 EditItemTemplate 和 InsertItemTemplate 部分中的名字和姓氏字段的所有 RequiredFieldValidator。

    <EditItemTemplate>
      <fieldset>
         <p><asp:Label runat="server" AssociatedControlID="firstName">First Name: </asp:Label></p>
         <p><asp:TextBox runat="server" ID="firstName" Text='<%#: BindItem.FirstName %>' />
            &nbsp;<asp:RequiredFieldValidator runat="server" ControlToValidate="firstName" ErrorMessage="Please enter a value for First Name" ForeColor="Red" />
        </p>
    
         <p><asp:Label runat="server" AssociatedControlID="lastName">Last Name: </asp:Label></p>
         <p><asp:TextBox runat="server" ID="lastName" Text='<%#: BindItem.LastName %>' />
              &nbsp;<asp:RequiredFieldValidator runat="server" ControlToValidate="lastName" ErrorMessage="Please enter a value for Last Name" ForeColor="Red" />
        </p>
      ...
    <InsertItemTemplate>        
     <fieldset>
       <p><asp:Label runat="server" AssociatedControlID="firstName">First Name: </asp:Label></p>
       <p><asp:TextBox runat="server" ID="firstName" Text='<%#: BindItem.FirstName %>' />           
         &nbsp;<asp:RequiredFieldValidator runat="server" ControlToValidate="firstName" ErrorMessage="Please enter a value for First Name" ForeColor="Red" />
        </p>
    
       <p><asp:Label runat="server" AssociatedControlID="lastName">Last Name: </asp:Label></p>                
        <p><asp:TextBox runat="server" ID="lastName" Text='<%#: BindItem.LastName %>' />
         &nbsp;<asp:RequiredFieldValidator runat="server" ControlToValidate="lastName" ErrorMessage="Please enter a value for Last Name" ForeColor="Red" />
        </p>
      ...
    

    注意

    使用数据注释的一个优点是验证逻辑不会在应用程序页中重复。 在模型中定义一次,并在操作数据的所有应用程序页中使用它。

  4. 打开 CustomerDetails.aspx 代码隐藏并找到 SaveCustomer 方法。 此方法在插入新客户时调用,并从 FormView 控件值接收 Customer 参数。 当页面控件与参数对象之间发生映射时,ASP.NET 将针对所有数据注释属性执行模型验证,并在 ModelState 字典中填充遇到的错误(如果有)。

    只有在执行验证后模型上的所有字段都有效时,ModelState.IsValid 才会返回 true。

    public void SaveCustomer(Customer customer) 
    {
        if (this.ModelState.IsValid)
        { 
            using (var db = new ProductsContext())
            {
                ...
    
  5. 在 CustomerDetails 页的末尾添加 ValidationSummary 控件以显示模型错误列表。

    </fieldset>
        </InsertItemTemplate>
      </asp:FormView>
    
      <asp:ValidationSummary runat="server" ShowModelStateErrors="true" 
           ForeColor="Red" HeaderText="Please check the following errors:"/>
    </asp:Content>
    

    ShowModelStateErrors 是 ValidationSummary 控件上的一个新属性,当设置为 true 时,控件将显示 ModelState 字典中的错误。 这些错误来自数据注释验证。

  6. F5 运行 Web 应用程序。 使用一些错误值填写表单,然后单击“ 保存 ”以执行验证。 请注意底部的错误摘要。

    使用数据注释进行

    使用数据批注进行验证

任务 3 - 使用 ModelState 处理自定义数据库错误

在以前版本的 Web Forms 中,处理数据库错误(如字符串过长或唯一键冲突)可能涉及在存储库代码中引发异常,然后处理代码隐藏上的异常以显示错误。 执行相对简单的操作需要大量的代码。

在 Web Forms 4.5 中,ModelState 对象可用于以一致的方式在页面上显示来自模型或数据库的错误。

在此任务中,你将添加代码以正确处理数据库异常,并使用 ModelState 对象向用户显示相应的消息。

  1. 当应用程序仍在运行时,请尝试使用重复值更新类别的名称。

    使用重复名称更新类别

    使用重复名称更新类别

    请注意,由于 CategoryName 列的“唯一”约束而引发异常。

    重复类别名称例外 重复

    重复类别名称的异常

  2. 停止调试。 在 Products.aspx.cs 代码隐藏文件中,更新 UpdateCategory 方法以处理数据库引发的异常。SaveChanges () 方法调用并将错误添加到 ModelState 对象。

    新的 TryUpdateModel 方法使用用户提供的表单数据更新从数据库中检索到的类别对象。

    (代码片段 - Web Forms实验室 - Ex02 - UpdateCategory 处理错误)

    public void UpdateCategory(int categoryId)
    {
      var category = this.db.Categories.Find(categoryId);
    
      this.TryUpdateModel(category);
    
      if (this.ModelState.IsValid)
      {
        try
        {
          this.db.SaveChanges();
        }
        catch (DbUpdateException)
        {
          var message = string.Format("A category with the name {0} already exists.", category.CategoryName);
          this.ModelState.AddModelError("CategoryName", message);
        }
      }
    }
    

    注意

    理想情况下,必须确定 DbUpdateException 的原因,如果根本原因是违反唯一键约束,则检查。

  3. 打开 Products.aspx 并在类别 GridView 下方添加 ValidationSummary 控件,以显示模型错误列表。

    <asp:GridView ID="categoriesGrid" runat="server"
      ...
    </asp:GridView>
    
    <asp:ValidationSummary ID="ValidationSummary1" runat="server" ShowModelStateErrors="true" />
    
    <h3>Products</h3>
    
  4. 运行站点并转到“产品”页。 尝试使用重复值更新类别的名称。

    请注意,已处理异常,错误消息显示在 ValidationSummary 控件中。

    重复类别错误

    类别重复错误

任务 4 - ASP.NET Web Forms 4.5 中的请求验证

ASP.NET 中的请求验证功能为跨站点脚本 (XSS) 攻击提供一定级别的默认保护。 在以前版本的 ASP.NET 中,请求验证默认处于启用状态,并且只能对整个页面禁用。 使用新版本的 ASP.NET Web Forms现在可以禁用单个控件的请求验证、执行延迟请求验证或访问未经验证的请求数据, (执行此操作时要小心!) 。

  1. Ctrl+F5 在不调试的情况下启动站点,然后转到“产品”页。 选择一个类别,然后单击任何产品上的 “编辑” 链接。

  2. 键入包含潜在危险内容的说明,例如,包括 HTML 标记。 请注意由于请求验证而引发的异常。

    编辑具有潜在危险内容的产品

    编辑包含潜在危险内容的产品

    由于请求验证引发

    由于请求验证而引发的异常

  3. 关闭页面,并在 Visual Studio 中按 Shift+F5 停止调试。

  4. 打开 ProductDetails.aspx 页并找到 “说明 ”文本框。

  5. 将新的 ValidateRequestMode 属性添加到 TextBox,并将其值设置为 Disabled

    使用新的 ValidateRequestMode 属性,可以精细地对每个控件禁用请求验证。 如果要使用可能接收 HTML 代码的输入,但希望使验证在页面的其余部分正常工作,这非常有用。

    <p>
      <asp:TextBox runat="server" ID="Description" TextMode="MultiLine" 
                Cols="60" Rows="8" Text='<%# BindItem.Description %>' 
        ValidateRequestMode="Disabled" />
    </p>
    
  6. F5 运行 Web 应用程序。 再次打开“编辑产品”页面,并完成产品说明(包括 HTML 标记)。 请注意,现在可以向说明添加 HTML 内容。

    产品说明禁用请求验证 产品

    产品说明的请求验证已禁用

    注意

    在生产应用程序中,应清理用户输入的 HTML 代码,以确保仅输入安全的 HTML 标记 (例如,没有 <脚本> 标记) 。 为此,可以使用 Microsoft Web 保护库

  7. 再次编辑产品。 在“名称”字段中键入 HTML 代码,然后单击“ 保存”。 请注意,仅对“说明”字段禁用“请求验证”,其余字段仍会针对潜在危险内容重新验证。

    在其余字段中启用

    在其余字段中启用请求验证

    ASP.NET Web Forms 4.5 包括一个新的请求验证模式,用于延迟执行请求验证。 请求验证模式设置为 4.5 时,如果一段代码访问 Request.Form[“key”],ASP.NET 4.5 的请求验证将仅触发表单集合中该特定元素的请求验证。

    此外,ASP.NET 4.5 现在包括 Microsoft Anti-XSS 库 v4.0 中的核心编码例程。 反 XSS 编码例程由新的 System.Web.Security.AntiXss 命名空间中的新 AntiXssEncoder 类型实现。 将 encoderType 参数配置为使用 AntiXssEncoder 后,ASP.NET 中的所有输出编码都自动使用新的编码例程。

  8. ASP.NET 4.5 请求验证还支持对请求数据的未经验证访问。 ASP.NET 4.5 向名为 UnvalidatedHttpRequest 对象添加新的集合属性。 导航到 HttpRequest.Unvalidated 时,可以访问所有常见的请求数据片段,包括窗体、QueryStrings、Cookie、URL 等。

    Request.Unvalidated 对象

    Request.Unvalidated 对象

    注意

    请谨慎使用 HttpRequest.Unvalidated 属性! 请确保仔细对原始请求数据执行自定义验证,以确保危险文本不会舍入并呈现回毫无戒备的客户!

练习 3:ASP.NET Web Forms中的异步页面处理

在本练习中,你将了解 ASP.NET Web Forms 中新的异步页面处理功能。

任务 1 - 更新产品详细信息页面以上传和显示图像

在此任务中,你将更新产品详细信息页面,以允许用户指定产品的图像 URL 并将其显示在只读视图中。 你将通过同步下载指定映像创建本地副本。 在下一个任务中,你将更新此实现,使其异步工作。

  1. 打开 Visual Studio 2012,并从此实验室的文件夹加载 Source\Ex3-Async\Begin 中的 Begin解决方案。 或者,可以继续使用前面的练习中的现有解决方案。

    1. 如果打开了提供的 Begin 解决方案,则需要下载一些缺少的 NuGet 包,然后才能继续。 为此,请在解决方案资源管理器中单击“WebFormsLab”项目,然后选择“管理 NuGet 包”。

    2. “管理 NuGet 包 ”对话框中,单击“ 还原 ”以下载缺少的包。

    3. 最后,单击“生成 | 解决方案”生成解决方案

      注意

      使用 NuGet 的优点之一是,无需交付项目中的所有库,从而减小了项目大小。 使用 NuGet Power Tools,通过在 Packages.config 文件中指定包版本,你将能够在首次运行项目时下载所有必需的库。 这就是在从本实验室打开现有解决方案后必须运行这些步骤的原因。

  2. 打开 ProductDetails.aspx 页面源,并在 FormView 的 ItemTemplate 中添加字段以显示产品图像。

    <ItemTemplate>
         <fieldset>
              <p><b><asp:Label ID="Label2" runat="server" AssociatedControlID="itemProductName">Name:</asp:Label></b></p>
              <p><asp:Label runat="server" ID="itemProductName" Text='<%#: Item.ProductName %>' /></p>
              <p><b><asp:Label ID="Label3" runat="server" AssociatedControlID="itemDescription">Description (HTML):</asp:Label></b></p>
              <p><asp:Label runat="server" ID="itemDescription" Text='<%# Item.Description %>' /></p>
              <p><b><asp:Label ID="Label4" runat="server" AssociatedControlID="itemUnitPrice">Price:</asp:Label></b></p>
              <p><asp:Label runat="server" ID="itemUnitPrice" Text='<%#: Item.UnitPrice %>' /></p>
    
              <p><b><asp:Label ID="Label5" runat="server" AssociatedControlID="itemUnitPrice">Image:</asp:Label></b></p>
              <p>
                    <img src="<%# string.IsNullOrEmpty(Item.ImagePath) ? "/Images/noimage.jpg" : 
                    Item.ImagePath %>" alt="Image" />
              </p>
    
              <br />
              <p>
                    <asp:Button ID="Button1" runat="server" CommandName="Edit" Text="Edit" />&nbsp;
                    <asp:HyperLink NavigateUrl="~/Products.aspx" Text="Back" runat="server" />
              </p>
         </fieldset>
    </ItemTemplate>
    
  3. 添加字段以在 FormView 的 EditTemplate 中指定图像 URL。

    <fieldset>
         <p><asp:Label ID="Label2" runat="server" AssociatedControlID="ProductName">Name:</asp:Label></p>
         <p><asp:TextBox runat="server" ID="ProductName" Text='<%#: BindItem.ProductName %>' /></p>
         <p><asp:Label ID="Label3" runat="server" AssociatedControlID="Description">Description (HTML):</asp:Label></p>
         <p>
              <asp:TextBox runat="server" ID="Description" TextMode="MultiLine" Cols="60" Rows="8" Text='<%# BindItem.Description %>'
                    ValidateRequestMode="Disabled" />
         </p>
         <p><asp:Label ID="Label4" runat="server" AssociatedControlID="UnitPrice">Price:</asp:Label></p>
         <p><asp:TextBox runat="server" ID="UnitPrice" Text='<%#: BindItem.UnitPrice %>' /></p>
    
         <p><asp:Label ID="Label1" runat="server" AssociatedControlID="ImagePath">Image URL:</asp:Label></p>
         <p><asp:TextBox runat="server" ID="ImagePath" Text='<%#:  BindItem.ImagePath %>' /></p>
    
         <br />
         <p>
              <asp:Button runat="server" CommandName="Update" Text="Save" />
              <asp:Button runat="server" CommandName="Cancel" Text="Cancel" CausesValidation="false" />
         </p>
    </fieldset>
    
  4. 打开 ProductDetails.aspx.cs 代码隐藏文件并添加以下命名空间指令。

    (代码片段 - Web Forms 实验室 - Ex03 - 命名空间)

    using System.IO;
    using System.Net;
    using System.Web;
    
  5. 创建 UpdateProductImage 方法,将远程图像存储在本地 Images 文件夹中,并使用新的映像位置值更新产品实体。

    (代码片段 - Web Forms实验室 - Ex03 - UpdateProductImage)

    private void UpdateProductImage(Product product)
    {
        string imageUrl = product.ImagePath;
    
        if (!string.IsNullOrEmpty(imageUrl) && !VirtualPathUtility.IsAbsolute(imageUrl))
        {
            product.ImagePath = string.Format(
                                     "/Images/{0}{1}", 
                                     product.ProductId, 
                                     Path.GetExtension(imageUrl));
    
            using (var wc = new WebClient())
            {
                wc.DownloadFile(imageUrl, Server.MapPath(product.ImagePath));
            }
        }
    }
    
  6. 更新 UpdateProduct 方法以调用 UpdateProductImage 方法。

    (代码片段 - Web Forms 实验室 - Ex03 - UpdateProductImage 调用)

    public void UpdateProduct(int productId)
    {
        var product = this.db.Products.Find(productId);
    
        this.TryUpdateModel(product);
    
        this.UpdateProductImage(product);
    
        if (this.ModelState.IsValid)
        {
            this.db.SaveChanges();
        }
    }
    
  7. 运行应用程序并尝试上传产品的图像。

    为产品设置图像

    设置产品的图像

任务 2 - 将异步处理添加到产品详细信息页

在此任务中,你将更新产品详细信息页面,使其异步工作。 你将使用 ASP.NET 4.5 异步页面处理来增强长时间运行的任务(映像下载过程)。

Web 应用程序中的异步方法可用于优化 ASP.NET 线程池的使用方式。 在 ASP.NET 线程池中用于处理请求的线程数有限,因此,当所有线程都处于繁忙状态时,ASP.NET 开始拒绝新请求、发送应用程序错误消息并使站点不可用。

网站上耗时的操作非常适合异步编程,因为它们长时间占用分配的线程。 这包括长时间运行的请求、具有许多不同元素的页面和需要脱机操作的页面,例如查询数据库或访问外部 Web 服务器。 优点是,如果使用异步方法执行这些操作,则在处理页面时,线程将被释放并返回到线程池,并可用于处理新的页面请求。 这意味着,页面将在线程池中的一个线程中开始处理,并且可能在异步处理完成后在不同的线程中完成处理。

  1. 打开 ProductDetails.aspx 页。 在 Page 元素中添加 Async 属性并将其设置为 true。 此属性告知 ASP.NET 实现 IHttpAsyncHandler 接口。

    <%@ Page Title="" Language="C#" MasterPageFile="~/Site.Master" AutoEventWireup="true"
        CodeBehind="ProductDetails.aspx.cs" Inherits="WebFormsLab.ProductDetails"
        Async="true" %>
    
  2. 在页面底部添加标签,以显示运行页面的线程的详细信息。

    <EmptyDataTemplate>Product not found</EmptyDataTemplate>
      </asp:FormView>
    
      <asp:Label ID="threadsMessageLabel" runat="server" />
    </asp:Content>
    
  3. 打开 ProductDetails.aspx.cs 并添加以下命名空间指令。

    (代码片段 - Web Forms 实验室 - Ex03 - 命名空间 2)

    using System.Web.UI;
    using System.Threading;
    
  4. 修改 UpdateProductImage 方法以使用异步任务下载映像。 你将将 WebClientDownloadFile 方法替换为 DownloadFileTaskAsync 方法,并包含 await 关键字 (keyword) 。

    (代码片段 - Web Forms实验室 - Ex03 - UpdateProductImage Async)

    private void UpdateProductImage(Product product)
    {
        string imageUrl = product.ImagePath;
    
        if (!string.IsNullOrEmpty(imageUrl) && !VirtualPathUtility.IsAbsolute(imageUrl))
        {
            product.ImagePath = string.Format(
                "/Images/{0}{1}", 
                product.ProductId, 
                Path.GetExtension(imageUrl));
    
            this.RegisterAsyncTask(new PageAsyncTask(async (t) =>
            {
                using (var wc = new WebClient())
                {
                    await wc.DownloadFileTaskAsync(imageUrl, this.Server.MapPath(product.ImagePath));
                }
            }));
        }
    }
    

    RegisterAsyncTask 注册要在不同的线程中执行的新页面异步任务。 它接收一个 lambda 表达式,其中包含要执行的 Task (t) 。 DownloadFileTaskAsync 方法中的 await 关键字 (keyword) 会将方法的其余部分转换为在 DownloadFileTaskAsync 方法完成后异步调用的回调。 ASP.NET 将通过自动维护所有 HTTP 请求原始值来恢复方法的执行。 .NET 4.5 中的新异步编程模型使你能够编写看起来非常类似于同步代码的异步代码,并允许编译器处理回调函数或延续代码的复杂问题。

    注意

    RegisterAsyncTask 和 PageAsyncTask 自 .NET 2.0 起已可用。 await 关键字 (keyword) 是 .NET 4.5 异步编程模型中的新增功能,可与 .NET WebClient 对象中的新 TaskAsync 方法一起使用。

  5. 添加代码以显示开始和完成执行代码的线程。 为此,请使用以下代码更新 UpdateProductImage 方法。

    (代码片段 - Web Forms实验室 - Ex03 - 显示线程)

    private void UpdateProductImage(Product product)
    {
      string imageUrl = product.ImagePath;
    
      if (!string.IsNullOrEmpty(imageUrl) && !VirtualPathUtility.IsAbsolute(imageUrl))
      {
        product.ImagePath = string.Format(
             "/Images/{0}{1}", 
             product.ProductId, 
             Path.GetExtension(imageUrl));
    
        this.RegisterAsyncTask(new PageAsyncTask(async (t) =>
        {
          var startThread = Thread.CurrentThread.ManagedThreadId;
    
          using (var wc = new WebClient())
          {
            await wc.DownloadFileTaskAsync(imageUrl, this.Server.MapPath(product.ImagePath));
          }
    
          var endThread = Thread.CurrentThread.ManagedThreadId;
    
          this.threadsMessageLabel.Text = string.Format("Started on thread: {0}<br /> Finished on thread: {1}", startThread, endThread);
        }));
      }
    }
    
  6. 打开网站的 Web.config 文件。 添加以下 appSetting 变量。

    <add key="aspnet:UseTaskFriendlySynchronizationContext" value="true"/>
    
  7. F5 运行应用程序并上传产品的图像。 请注意,代码启动和完成位置的线程 ID 可能不同。 这是因为异步任务在与 ASP.NET 线程池不同的线程上运行。 任务完成后,ASP.NET 将任务放回队列并分配任何可用线程。

    异步下载映像

    异步下载映像

注意

此外,可以按照 附录 B:使用 Web 部署发布 ASP.NET MVC 4 应用程序,将此应用程序部署到 Azure。


总结

在本动手实验室中,已处理并演示了以下概念:

  • 使用强类型数据绑定表达式
  • 在 Web Forms 中使用新的模型绑定功能
  • 使用值提供程序将页面数据映射到代码隐藏方法
  • 使用数据注释进行用户输入验证
  • 在 Web Forms 中利用 jQuery 的不显眼的客户端验证
  • 实现精细请求验证
  • 在 Web Forms 中实现异步页面处理

附录 A:安装 Visual Studio Express 2012 for Web

可以使用 Microsoft Web 平台安装程序 安装 Microsoft Visual Studio Express 2012 for Web 或其他“Express”版本。 以下说明将指导你完成使用 Microsoft Web 平台安装程序 安装 Visual Studio Express 2012 for Web 所需的步骤。

  1. 转到 [https://go.microsoft.com/?linkid=9810169] (https://go.microsoft.com/?linkid=9810169) 。 或者,如果已安装 Web 平台安装程序,则可以打开它并搜索产品“Visual Studio Express 2012 for Web with Azure SDK”。

  2. 单击“ 立即安装”。 如果没有 Web 平台安装程序 ,将重定向到先下载并安装它。

  3. Web 平台安装程序打开后,单击“安装”开始安装。

    安装 Visual Studio Express

    安装 Visual Studio Express

  4. 阅读所有产品的许可证和条款,然后单击“ 我接受 ”继续。

    接受许可条款

    接受许可条款

  5. 等待下载和安装过程完成。

    安装进度

    安装进度

  6. 安装完成后,单击“ 完成”。

    安装已完成

    安装已完成

  7. 单击“ 退出 ”关闭 Web 平台安装程序。

  8. 若要打开 web Visual Studio Express,请转到“开始”屏幕并开始编写“VS Express”,然后单击 VS Express for Web 磁贴。

    VS Express for Web 磁贴

    VS Express for Web 磁贴

附录 B:使用 Web 部署发布 ASP.NET MVC 4 应用程序

本附录将介绍如何从 Azure 门户创建新网站,并利用 Azure 提供的 Web 部署发布功能,发布通过实验室获取的应用程序。

任务 1 - 从 Azure 门户创建新网站

  1. 转到 Azure 管理门户 ,并使用与订阅关联的 Microsoft 凭据登录。

    注意

    借助 Azure,可以免费托管 10 个 ASP.NET 网站,然后随着流量的增长而缩放。 可以 在此处注册。

    登录到 Windows Azure 门户

    登录到门户

  2. 单击命令栏上的“ 新建 ”。

    创建新网站

    创建新网站

  3. 单击“ 计算 | 网站”。 然后选择“ 快速创建 ”选项。 提供新网站的可用 URL,然后单击“ 创建网站”。

    注意

    Azure 是在云中运行的 Web 应用程序的主机,可以控制和管理。 使用“快速创建”选项,可以从门户外部将已完成的 Web 应用程序部署到 Azure。 它不包括设置数据库的步骤。

    使用快速创建创建新网站

    使用“快速创建”创建新网站

  4. 等到创建新 网站

  5. 创建网站后,单击 URL 列下的链接。 检查新网站是否正常工作。

    浏览到新网站

    浏览到新网站

    运行网站的网站

    网站正在运行

  6. 返回门户并单击“名称”列下的网站名称以显示管理页。

    打开网站管理页

    打开网站管理页

  7. “仪表板” 页的 “快速概览 ”部分下,单击“ 下载发布配置文件 ”链接。

    注意

    发布配置文件包含每个已启用发布方法将 Web 应用程序发布到 Azure 所需的所有信息。 发布配置文件包含有连接到并且验证该发布方法启用的每个端点所需的 URL、用户凭据和数据库字符串。 Microsoft WebMatrix 2Microsoft Visual Studio Express for WebMicrosoft Visual Studio 2012 支持读取发布配置文件,以自动配置这些程序,以便将 Web 应用程序发布到 Azure。

    下载网站发布配置文件

    下载网站发布配置文件

  8. 将发布配置文件下载到已知位置。 在本练习中,你将进一步了解如何使用此文件从 Visual Studio 将 Web 应用程序发布到 Azure。

    保存发布配置文件

    保存发布配置文件

任务 2 - 配置数据库服务器

如果应用程序使用SQL Server数据库,则需要创建SQL 数据库服务器。 如果要部署不使用 SQL Server的简单应用程序可以跳过此任务。

  1. 需要一个SQL 数据库服务器来存储应用程序数据库。 可以在 Azure 管理门户中的 Sql 数据库服务器 | | 服务器的仪表板中查看订阅中的SQL 数据库服务器。 如果没有创建服务器,可以使用命令栏上的 “添加” 按钮创建一个服务器。 记下 服务器名称和 URL、管理员登录名和密码,因为将在后续任务中使用它们。 暂时不要创建数据库,因为稍后会创建该数据库。

    SQL 数据库服务器仪表板

    SQL 数据库服务器仪表板

  2. 在下一个任务中,你将测试来自 Visual Studio 的数据库连接,因此需要在服务器的“允许的 IP 地址”列表中包括本地 IP 地址。 为此,请单击“ 配置”,从“ 当前客户端 IP 地址”中选择 IP 地址 ,并将其粘贴到“ 开始 IP 地址 ”和“ 结束 IP 地址 ”文本框中,然后单击 add-client-ip-address-ok-button

    添加客户端 IP 地址

    添加客户端 IP 地址

  3. 客户端 IP 地址 添加到允许的 IP 地址列表后,单击“ 保存 ”以确认更改。

    确认更改

    确认更改

任务 3 - 使用 Web 部署发布 ASP.NET MVC 4 应用程序

  1. 返回 ASP.NET MVC 4 解决方案。 在解决方案资源管理器,右键单击网站项目,然后选择“发布”。

    发布应用程序

    发布网站

  2. 导入在第一个任务中保存的发布配置文件。

    导入发布配置文件

    导入发布配置文件

  3. 单击“ 验证连接”。 验证完成后,单击“ 下一步”。

    注意

    在“验证连接”按钮旁边显示绿色复选标记后,验证已完成。

    验证连接

    验证连接

  4. “设置” 页的“ 数据库 ”部分下,单击数据库连接的文本框旁边的按钮, (即 DefaultConnection) 。

    Web 部署配置

    Web 部署配置

  5. 按如下所示配置数据库连接:

    • “服务器名称”中,使用 tcp: 前缀键入SQL 数据库服务器 URL。

    • “用户名” 中,键入服务器管理员登录名。

    • “密码” 中,键入服务器管理员登录密码。

    • 键入新的数据库名称。

      配置目标连接字符串

      配置目标连接字符串

  6. 然后单击“确定”。 当系统提示创建数据库时,单击“ ”。

    创建数据库

    创建数据库

  7. 用于连接到 Azure 中SQL 数据库的连接字符串显示在“默认连接”文本框中。 然后单击“下一步”。

    指向 SQL 数据库 SQL 数据库 的连接字符串

    指向SQL 数据库的连接字符串

  8. “预览 ”页中,单击“ 发布”。

    发布 Web 应用程序

    发布 Web 应用程序

  9. 发布过程完成后,默认浏览器将打开已发布的网站。

附录 C:使用代码片段

使用代码片段时,你只需触手可及的所有代码。 实验室文档将确切地告诉你何时可以使用它们,如下图所示。

使用 Visual Studio 代码片段将代码插入到项目中

使用 Visual Studio 代码片段将代码插入到项目中

若要使用键盘 (C# 添加代码片段,请仅)

  1. 将光标置于要插入代码的位置。
  2. 开始键入代码片段名称 (,) 不带空格或连字符。
  3. 观看 IntelliSense 显示匹配的代码片段名称。
  4. 选择正确的代码段 (或继续键入,直到) 选择整个代码段的名称。
  5. 按 Tab 键两次,将代码片段插入光标位置。

开始键入代码段名称

开始键入代码片段名称

按 Tab 选择突出显示的代码段

按 Tab 选择突出显示的代码段

再次按 Tab,代码段将

再次按 Tab,代码片段将展开

若要使用鼠标 (C#、Visual Basic 和 XML 添加代码片段,) 1. 右键单击要插入代码片段的位置。

  1. 选择“ 插入代码段 ”,然后选择 “我的代码片段”。
  2. 通过单击从列表中选择相关代码片段。

右键单击要插入代码段的位置,然后选择“插入代码段

右键单击要插入代码片段的位置,然后选择“插入代码段”

从列表中选择相关代码段,方法是单击它

从列表中选择相关代码片段,方法是单击它