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

作者: Web Camp 团队

新版本的 ASP.NET Web 窗体引入了一些改进,重点是在处理数据时改善用户体验。

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

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

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

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

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

  • TextBox 控件的 TextMode 属性已更新,以支持新的 HTML5 输入类型,例如电子邮件、日期时间等。
  • FileUpload 控件现在支持从支持此 HTML5 功能的浏览器上传多个文件。
  • 验证程序控件现在支持验证 HTML5 输入元素。
  • 具有表示 URL 的属性的新 HTML5 元素现在支持 runat=“server”。 因此,可以在 URL 路径中使用 ASP.NET 约定,例如 ~ 运算符来表示应用程序根(例如 <,视频 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 窗体中使用新的模型绑定功能
  • 使用值提供程序将页面数据映射到代码隐藏方法
  • 使用数据注释进行用户输入验证
  • 在 Web 窗体中使用 jQuery 利用不显眼的客户端验证
  • 实现精细请求验证
  • 在 Web 窗体中实现异步页面处理

先决条件

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

安装

安装代码片段

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

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

练习

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

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

注意

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

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

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

新版本的 ASP.NET Web 窗体引入了一些增强功能,重点是改进处理数据时的体验。 在本练习中,你将了解强类型化数据控件和模型绑定。

任务 1 - 使用强类型数据绑定

在此任务中,你将发现 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 页。 将未编号的列表放在主控件中,并在其中包括一个重复程序控件,用于列出每个客户。 将重复程序名称设置为 customersRepeater ,如以下代码所示。

    在以前版本的 Web 窗体中,使用数据绑定在要绑定到的对象上发出成员的值时,将使用数据绑定表达式以及对 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 窗体实验室 - 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 可用于响应写入,但现在也可用于数据绑定表达式。

    注意

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

    强类型绑定中的 IntelliSense 支持

    强类型绑定中的 IntelliSense 支持

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

    列出客户详细信息

    列出客户详细信息

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

任务 2 - 在 Web 窗体中引入模型绑定

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

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

若要了解这一点,你将使用 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 窗体实验室 - 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 窗体实验室 - Ex01 - GetCategories

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

    注意

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

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

    使用模型绑定填充 GridView

    使用模型绑定填充 GridView

  7. SHIFT+F5 停止调试。

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

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

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

如果已使用 ASP.NET MVC 4,你会注意到模型绑定支持类似。 事实上,这些功能是从 ASP.NET MVC 中获取的,并移入 System.Web 程序集,以便能够在 Web 窗体上使用这些功能。

在此任务中,你将更新 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 窗体实验室 - 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 窗体实验室 - Ex01 - GetProducts

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

    注意

    使用此方法可以更轻松地对这些方法进行单元测试。 在未执行 Web 窗体的单元测试上下文中,[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 窗体实验室 - 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 窗体实验室 - 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 窗体中查看新的不显眼的验证功能。 你将在应用程序模型类中使用数据注释进行用户输入验证,最后,你将了解如何打开或关闭对页面中各个控件的请求验证。

任务 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 属性。 这将强制 jQuery 从联机 URL 加载,而不是从本地网站的 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 代码替换为数据 - *属性。

    不显眼的验证代码

    不显眼的验证代码

    注意

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

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

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

  1. 打开 CustomerDetail.aspx 页。 请注意,使用 RequiredFieldValidator 控件验证 EditItemTemplate 和 InsertItemTemplate 部分中的客户名字和第二个名称 每个验证程序都与特定条件相关联,因此需要包含任意数量的验证程序作为条件进行检查。

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

    (代码片段 - Web 窗体实验室 - 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]。

    一些用法示例:

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

    [Range(0.4, 0.5, ErrorMessage=“{Write an error message}”]:Double range

    [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 窗体中,处理数据库错误(如字符串过长或唯一键冲突)可能涉及在存储库代码中引发异常,然后处理代码隐藏上的异常以显示错误。 执行相对简单的操作需要大量的代码。

在 Web Forms 4.5 中,ModelState 对象可用于以一致的方式在页面上显示错误,无论是从模型还是数据库。

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

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

    使用重复名称更新类别

    使用重复名称更新类别

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

    重复类别名称的异常

    重复类别名称的异常

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

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

    (代码片段 - Web 窗体实验室 - 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 窗体 4.5 中的请求验证

ASP.NET 中的请求验证功能提供了针对跨站点脚本(XSS)攻击的一定级别的默认保护。 在以前版本的 ASP.NET 中,默认启用请求验证,并且只能对整个页面禁用。 使用新版本的 ASP.NET Web 窗体,现在可以禁用单个控件的请求验证,执行延迟请求验证或访问未经验证的请求数据(如果这样做!)。

  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 Library v4.0 的核心编码例程。 反 XSS 编码例程由新的 System.Web.Security.AntiXss 命名空间中找到的新 AntiXssEncoder 类型实现。 将 encoderType 参数配置为使用 AntiXssEncoder 后,ASP.NET 中的所有输出编码会自动使用新的编码例程。

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

    Request.Unvalidated 对象

    Request.Unvalidated 对象

    注意

    请谨慎使用 HttpRequest.Unvalidated 属性! 请确保仔细对原始请求数据执行自定义验证,以确保危险文本未轮换并呈现回不知情的客户!

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

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

任务 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 窗体实验室 - Ex03 - 命名空间

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

    (代码片段 - Web 窗体实验室 - 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 窗体实验室 - 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 窗体实验室 - Ex03 - 命名空间 2

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

    (代码片段 - Web 窗体实验室 - 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 表达式,其中包含要执行的任务 (t)。 DownloadFileTaskAsync 方法中的 await 关键字将该方法的其余部分转换为在 DownloadFileTaskAsync 方法完成后异步调用的回调。 ASP.NET 将通过自动维护所有 HTTP 请求原始值来恢复方法的执行。 使用 .NET 4.5 中的新异步编程模型,可以编写与同步代码非常相似的异步代码,并允许编译器处理回调函数或延续代码的复杂性。

    注意

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

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

    (代码片段 - Web 窗体实验室 - 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 将此应用程序部署到 Azure:使用 Web 部署发布 ASP.NET MVC 4 应用程序。


总结

在此实践实验室中,已解决并演示了以下概念:

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

附录 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. 转到 [/iis/extensions/introduction-to-iis-express/iis-express-overview?linkid=9810169](/iis/extensions/introduction-to-iis-express/iis-express-overview?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. 若要打开 Visual Studio Express for Web,请转到“开始”屏幕并开始编写“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. 将发布配置文件下载到已知位置。 在本练习中,你将了解如何使用此文件将 Web 应用程序从 Visual Studio 发布到 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 数据库的连接字符串

  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. 通过单击相关代码片段从列表中选择相关代码片段。

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

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

通过单击相关代码片段从列表中选择相关代码片段

通过单击相关代码片段从列表中选择相关代码片段