添加更多的 DataTable 列69

简介

向 强类型 DataSet添加TableAdapter 时 ,对应DataTable 的schema 取决于TableAdapter 的主要查询。例如 ,如果主要查询返回A、B、C 三个数据字段 ,DataTable 将有三个名为 A 、B 、C 的对应列。除了其主要查询外,TableAdapter 还可以包含更多查询,这些查询可能返回基于某个参数的数据子集。例如,除了其返回关于所有产品信息的主要查询之外,ProductsTableAdapter 还可包含如GetProductsByCategoryID(categoryID) 及 GetProductByProductID(productID) 等方法,这些方法基于提供的参数返回特定产品的信息。

如果TableAdapter 的所有方法返回的数据字段等于或少于主要查询中指定的数据字段,用DataTable 的schema 反映 TableAdapter 的主要查询的方法则是可行的。但如果TableAdapter 方法需要返回更多数据字段,我们则应对DataTable 的 schema 进行相应扩展。在使用具有明细 DataList的主记录项目符号列表的主 /明细筛选 教程中,我们为 CategoriesTableAdapter 添加了一个方法,返回在主要查询中定义的CategoryID 、CategoryName 和 Description 数据字段以及 NumberOfProducts ,另外还有一个报告每个类别相关的产品数量的数据字段。我们向CategoriesDataTable 手动新添一列,以便从此新方法中获取NumberOfProducts 数据字段值。

正如在上载文件 教程中所述,对于使用 ad-hoc SQL 语句或其方法的数据字段不能精确匹配主要查询的TableAdapter ,必须加以小心。如果 TableAdapter Configuration 向导再次运行,它将更新TableAdapter 的所有方法,以使其数据字段列表匹配主要查询。因此,所有带有定制列列表的方法都将还原为主要查询的列列表,而不返回所需的数据。若使用存储过程,则不会出现上述问题。

本教程中,我们将学习如何扩展 DataTable 的schema 以包含更多列。由于使用 ad-hoc SQL 语句时TableAdapter 比较容易出现问题,本教程中将使用存储过程。欲了解关于配置TableAdapter 以使用存储过程的信息,请参考为强类型 DataSet的TableAdapters创建新的存储过程及对强类型 DataSet的 TableAdapter使用现有存储过程 教程。

步骤 1:向 ProductsDataTable添加 PriceQuartile 列

 为强类型DataSet的TableAdapter新建存储过程 教程中,我们创建了一个 名为 NorthwindWithSprocs 的强类型 DataSet 。此 DataSet 现在包含两个 DataTable :ProductsDataTable 和 EmployeesDataTable 。ProductsTableAdapter 有如下三个方法:

  • GetProducts :主要查询。返回 Products 表中的所有记录;
  • GetProductsByCategoryID(categoryID) :返回指定 categoryID 的所有产品;.
  • GetProductByProductID(productID) :返回指定 productID 的特定产品。

主要查询和两个新增的方法都返回同样的数据字段集,即 Products 表中的所有列。没有任何关联的子查询或JOIN 从 Categories 或 Suppliers 表中获取相关数据。因此,ProductsDataTable 对于 Products 表中的每一个字段都有一个对应列。

本教程中我们将向名为 GetProductsWithPriceQuartile 的 ProductsTableAdapter 添加一个方法,返回所有产品。除了标准的产品数据字段外,GetProductsWithPriceQuartile 还将包含一个 PriceQuartile 数据字段,指明产品价格所处的四分位数。例如,按价格从高到低,排在前 25% 的产品的 PriceQuartile 值将为 1,而最后 25% 的产品该值为 4。但在我们创建返回此信息的存储过程之前,需要首先更新ProductsDataTable以添加一列来放置使用GetProductsWithPriceQuartile 时的 PriceQuartile 结果。

打开 NorthwindWithSprocs DataSet 并右键单击 ProductsDataTable。从关联菜单中选择 Add,然后选择 Column。

图1:向 ProductsDataTable 添加新列

这将为 DataTable 添加一个名称为 Column1,类型为 System.String 的新列。由于此列将用于包含 1 至 4 的数字,我们需要将此列的名称和类型分别更新为 PriceQuartile 和 System.Int32 和。在 ProductsDataTable 中选中新增的列,然后在 Properties 窗口中将 Name 及 DataType 属性分别赋值为 PriceQuartile 和 System.Int32。

图2:为新列的 Name 和 DataType 属性赋值

如图 2 所示,在窗口中还可设置其他属性,如列中的值是否为唯一值、该列是否为自动增加的、是否允许数据库的 NULL 值等。保留这些属性默认值不变。

步骤 2:创建 GetProductsWithPriceQuartile 方法

现在更新后的ProductsDataTable 包含 PriceQuartile 列,接下来就可以创建 GetProductsWithPriceQuartile 方法了。首先右键单击 TableAdapter 并从关联菜单中选择Add Query ,将显示 TableAdapter Query Configuration 向导,首先提示我们选择使用ad-hoc SQL 语句还是新的或现有的存储过程。由于我们还没有返回价格四分位数数据的存储过程,因此需要用TableAdapter 创建此存储过程。选中Create new stored procedure 选项并单击 Next 。

图3 :在 TableAdapter 向导中选择创建存储过程

接下来出现的屏幕如图 4 所示,向导提示选择要添加的查询类型。由于GetProductsWithPriceQuartile 方法将返回 Products 表中的所有列和记录,所以选择 SELECT which returns rows 选项并单击Next 。

图4 :创建的查询为返回多个行的 SELECT 语句

然后提示输入SELECT 查询。在向导中输入如下查询:

SELECT ProductID, ProductName, SupplierID, CategoryID,  
       QuantityPerUnit, UnitPrice, UnitsInStock, UnitsOnOrder,  
       ReorderLevel, Discontinued, 
       NTILE(4) OVER (ORDER BY UnitPrice DESC) as PriceQuartile 
FROM Products

上述查询使用 SQL Server 2005 新增的 NTILE 功能 ,通过将 UnitPrice 值按降序排列,将结果分为 4 类。

遗憾的是,Query Builder 无法解析 OVER 关键字,因此在解析上述查询时将显示一条错误。因此,要直接在向导的文本框中直接输入上述查询,而不使用Query Builder 。

注意: 有关 NTILE 及 SQL Server 2005 其他排序功能的更多信息,请参阅 SQL Server 2005 联机资料 中的 使用 Microsoft SQL Server 2005 返回排序结果 和 排序功能 部分。

输入SELECT 查询并单击 Next 之后,向导将要求为即将创建的存储过程命名。将新建的存储过程命名为Products_SelectWithPriceQuartile 然后单击Next 。

图5 :将存储过程命名为 Products_SelectWithPriceQuartile

最后,向导提示为 TableAdapter 方法命名。保持勾选上的 Fill a DataTable 和Return a DataTable 复选框,并将方法命名为 FillWithPriceQuartile 和 GetProductsWithPriceQuartile 。

图6 :为 TableAdapter 的方法命名并单击Finish

指定了SELECT 查询并为存储过程和 TableAdapter 方法命名后,单击 Finish 完成向导。这时向导可能会出现一条或两条警告消息,提示The OVER SQL construct or statement is not supported (不支持OVER 构件或语句)。忽略这些警告即可。

完成向导后,TableAdapter 应包含 FillWithPriceQuartile 和 GetProductsWithPriceQuartile 方法,且数据库应包含一个名为 Products_SelectWithPriceQuartile 的存储过程。花点时间来确认 TableAdapter 确实已经包含此新增方法,且存储过程已被正确添加到数据库中。如果检查数据库时未看到存储过程,可以试试右键单击Stored Procedures 文件夹再选择Refresh 。

图7 :确认新方法已被添加到 TableAdapter 中

图8 :确认数据库中包含 Products_SelectWithPriceQuartile 存储过程

注意: 使用存储过程而非 ad-hoc SQL 语句的好处之一是再次运行TableAdapter Configuration 向导时将不会更改存储过程的列列表。确认方法为右键单击TableAdapter ,在关联菜单中选择Configure 以启动向导,然后单击 Finish 结束。然后,进入数据库查看 Products_SelectWithPriceQuartile 存储过程。注意,其列列表未被更改。如果我们使用的是ad-hoc SQL 语句,那么再次运行 TableAdapter Configuration 向导将导致还原该查询的列列表以匹配主要查询的列列表,从而移除GetProductsWithPriceQuartile 方法使用的查询的NTILE 语句。

当数据访问层的GetProductsWithPriceQuartile 方法被调用时,TableAdapter 将执行 Products_SelectWithPriceQuartile 存储过程,并针对每条返回的记录向 ProductsDataTable 添加一个行。存储过程返回的数据字段将被映射到ProductsDataTable 的列。由于存储过程返回了一个 PriceQuartile 数据字段,其值将被赋给 ProductsDataTable 的PriceQuartile 列。

对于其查询没有返回PriceQuartile 数据字段的 TableAdapter 方法,PriceQuartile 列的值为其 DefaultValue 属性指定的值。如图2 所示,此值被赋为默认值DBNull 。如果想使用默认值之外的值,只需相应地设置DefaultValue 属性即可。只是要确保列的 DataType 中DefaultValue 值是有效的(即 PriceQuartile 列的 System.Int32 )。

现在我们已经完成了向 DataTable 添加更多列的必需步骤。为验证该添加的列能如预期正常工作,我们创建一个ASP.NET 页面来显示每个产品的名称、价格和价格四分位数。但在此之前,首先需要更新业务逻辑层,使其包含一个方法,以向下调用DAL 的 GetProductsWithPriceQuartile 方法。我们将在步骤 3 中更新 BLL ,然后在步骤4 中 创建ASP.NET 页面。

步骤 3:增加业务逻辑层

在使用来自表示层的GetProductsWithPriceQuartile 新方法之前,应首先向 BLL 添加一个对应的方法。打开 ProductsBLLWithSprocs 类文件并添加如下代码:

[System.ComponentModel.DataObjectMethodAttribute 
    (System.ComponentModel.DataObjectMethodType.Select, false)] 
public NorthwindWithSprocs.ProductsDataTable GetProductsWithPriceQuartile() 

    return Adapter.GetProductsWithPriceQuartile(); 
}

与 ProductsBLLWithSprocs 中的其他数据检索方法一样,GetProductsWithPriceQuartile 方法只是调用 DAL 中对应的 GetProductsWithPriceQuartile 方法并返回其结果。

步骤 4:在 ASP.NET 网页中显示价格四分位数信息

完成向 BLL 的添加后,接下来就可以创建一个 ASP.NET 页面,显示每件产品的价格四分位数了。打开AdvancedDAL 文件夹中的AddingColumns.aspx 页面,从 Toolbox 中拖动一个 GridView 到 Designer上,并将其ID 属性赋值为Products。从 GridView 的智能标记中,将其与名为 ObjectDataSource 的新 ProductsDataSource 绑定。 配置 ObjectDataSource,使其使用 ProductsBLLWithSprocs 类的GetProductsWithPriceQuartile 方法。由于这个是只读网格,所以应将 UPDATE、INSERT 和 DELETE 选项卡上的下拉列表设为 (None)。

图9:配置 ObjectDataSource,使其使用 ProductsBLLWithSprocs 类

图10:从 GetProductsWithPriceQuartile 方法中检索产品信息

完成 Configure Data Source 向导后,Visual Studio 将自动向 GridView 为方法返回的每个数据字段添加一个 BoundField 或 CheckBoxField。其中一个数据字段是PriceQuartile,它是我们在步骤 1 中向 ProductsDataTable 添加的列。

编辑 GridView 的字段:移除除 ProductName、UnitPrice 和 PriceQuartile 外的所有 BoundField。配置UnitPriceBoundField,将其值设为货币格式,并分别将UnitPrice 和PriceQuartileBoundField 设置为右对齐和居中格式。最后,将其余的 BoundField 的 HeaderText 属性分别更新为 Product、Price 和 Price Quartile。此外,再勾选上 GridView 的智能标记上的 Enable Sorting 复选框。

完成这些更改后, GridView 和 ObjectDataSource 的声明式标记应为如下所示:

<asp:GridView ID="Products" runat="server" AllowSorting="True" AutoGenerateColumns="False" 
            DataKeyNames="ProductID" DataSourceID="ProductsDataSource"> 
            <Columns> 
                <asp:BoundField DataField="ProductName" HeaderText="Product" SortExpression="ProductName" /> 
                <asp:BoundField DataField="UnitPrice" DataFormatString="{0:c}" HeaderText="Price" 
                    HtmlEncode="False" SortExpression="UnitPrice"> 
                    <ItemStyle HorizontalAlign="Right" /> 
                </asp:BoundField> 
                <asp:BoundField DataField="PriceQuartile" HeaderText="Price Quartile" SortExpression="PriceQuartile"> 
                    <ItemStyle HorizontalAlign="Center" /> 
                </asp:BoundField> 
            </Columns> 
        </asp:GridView> 
        <asp:ObjectDataSource ID="ProductsDataSource" runat="server" OldValuesParameterFormatString="original_{0}" 
            SelectMethod="GetProductsWithPriceQuartile" TypeName="ProductsBLLWithSprocs"> 
        </asp:ObjectDataSource>

图 11 为通过浏览器访问时显示的此网页。注意,产品最初是按价格降序排列的,每个产品都指定一个适当的 PriceQuartile 值。当然,这一数据可以以其他标准排序,而 Price Quartile 列的值仍然反映产品的价格排序(如图 12 所示)。

图11:产品按价格排序

图12:产品按名称排序

注意:只需添加几行代码就可使 GridView 根据产品行的 PriceQuartile 值用彩色显示它们。例如,我们可以将第一个四分位数下的产品以浅绿色显示,第二个四分位数下的产品以浅黄色显示,以此类推。建议您花点时间增加此功能。如果需要回顾关于 GridView 格式设置的知识,请参阅 根据数据设置自定义格式教程。

其它方法 :创建另一个TableAdapter

如本教程中所述,向返回不同于主要查询的数据字段的 TableAdapter 添加方法时,可以向 DataTable 添加对应列。但这种方法只在 TableAdapter 中只有一小部分方法返回不同的数据字段、且这些数据字段与主要查询的差别并不太大的情况下适用。

除了向 DataTable 添加列之外,您还可以向 DataSet 另外添加一个 TableAdapter,使其包含返回不同数据字段的第一个 TableAdapter 中的方法。对于本教程,我们不向ProductsDataTable 添加PriceQuartile 列(只能被GetProductsWithPriceQuartile 方法使用),而是向 DataSet 添加另一个名为 ProductsWithPriceQuartileTableAdapter 的 TableAdapter,使其将 Products_SelectWithPriceQuartile 存储过程作为其主要查询使用。需要获取产品信息及其价格四分位数的 ASP.NET 页面将使用 ProductsWithPriceQuartileTableAdapter,而其它页面则继续使用ProductsTableAdapter。

通过新增一个 TableAdapter,DataTable 继续保持原样,它们的列精确地反映出其 TableAdapter 的方法返回的数据字段。但更多的 TableAdapter 可以引入重复任务和功能。例如,如果那些显示PriceQuartile 列的 ASP.NET 页面也需要提供插入、更新和删除支持,则需要正确配置ProductsWithPriceQuartileTableAdapter的InsertCommand、UpdateCommand 和DeleteCommand 属性。尽管这些属性映射了ProductsTableAdapter 的属性,但该配置涉及到另外一个步骤。而且,有两种方法可以对数据库中的产品进行更新、删除或添加操作:通过ProductsTableAdapter 类或ProductsWithPriceQuartileTableAdapter类进行。

本教程的下载资料中的 NorthwindWithSprocs Dataset 包含一个 ProductsWithPriceQuartileTableAdapter 类,其中介绍了这一方法。

小结

在大多数场景下,TableAdapter 中的所有方法都将返回相同的数据字段集。但有时候有一个或两个方法需要返回其他的字段。例如,在 使用具有 Details DataList 的主要记录项目符号列表的主/明细报表教程中我们向CategoriesTableAdapter 添加了一个方法,该方法除了返回主要查询的数据字段外,还返回一个NumberOfProducts 字段,报告每个类别相关的产品数量。本教程中,我们学习了在ProductsTableAdapter 中添加方法,除了返回主要查询的数据字段外还返回一个PriceQuartile 字段。为获取 TableAdapter 的方法返回的其他数据字段,我们需要向 DataTable 添加对应的列。

如果您打算手动向 DataTable添加列,建议 TableAdapter 使用存储过程。如果 TableAdapter 使用 ad-hoc SQL 语句,那么每次运行 TableAdapter Configuration 向导时,所有方法的数据字段列表都将还原为主要查询返回的数据字段。而使用存储过程则不会出现此问题,这就是本教程推荐使用存储过程且存储过程在本教程使用的原因。

快乐编程!

posted @ 2016-05-02 00:36  迅捷之风  阅读(134)  评论(0编辑  收藏  举报