戴南的ArcGIS's Blog
ArcGIS的开发应用

导航

 

Accessing and Updating Data in ASP.NET 2.0: Filtering Database Data with Parameters
By Scott Mitchell


译者:戴南,北京信息工程学院

原文地址:http://aspnet.4guysfromrolla.com/articles/030106-1.aspx

序:

ASP.NET 2.0中新近引入了一系列的用来访问和更新数据的WEB控件。这些控件的出现使得网页开发人员可以以声明式的编程方式访问并对数据进行操作,摈弃了原来那种编写数据访问代码的方式。本篇文章是我所写的关于ASP.NET 2.0中最新的数据源控件文章系列专题中的一个。

·数据源控件的基础知识 探究数据源控件的基本概念以及它的优点,并且与ASP.NET 1.x平台中的数据访问技术做了一个初步的比较。

·访问数据库中的数据 展示如何通过SqlDataSource以及AccessDataSource控件从一个关系形数据库中查询并获得数据。

·使用参数对数据库数据进行筛选 学习如何使用硬编码的参数值以及QueryString查询字符串传递来的参数值,或者是其他WEB控件传递的值,Session会话变量值等参数进行筛选,来获取一个数据库中的数据的子集。

·使用XmlDataSource控件访问XML数据 了解如何访问位于远程以及本地的XML数据并且将这些数据显示于WEB控件中。

·创建自定义的参数控件 学习如何创建自定义的、声明式的参数控件,该控件基于数据源控件的参数集合。

·了解数据源控件的事件机制 探究数据源控件的整个生命周期中,如何触发事件。


前言

在我们这个系列文章的第2讲中(也就是小白杨翻译的那篇),我们考虑了如何通过使用AccessDataSourceSqlDataSource对关系型数据库进行访问的相关问题。不过,在第2讲中,我们只考虑了如何获取表、视图、存储过程以及进行SQL即席查询(Ad-hoc SQL)的数据。通常来讲,我们一般只会获取按不同标准筛选过的数据子集。比如,当我们在亚马逊网站上搜索一本自己想要的书时,搜索页仅仅返回与你所输入的搜索条件相关的数据;当你想进一步察看某本书的具体情况的时候,也只会返回与这本书有关的查询信息。

 结果集是通过SQL语句中所含有布尔算数表达式的WHERE子句来进行筛选得到的。对于数据表中每一条记录,WHERE子句将评价并判断其是否应该包含于结果集合中。比如,如下SQL语句:SELECT ProductID,ProductName FROM Products WHERE UnitPrice<15.00会返回所有单价小于15.00的产品记录中ProductIdProductName列的值。

 SqlDataSourceAccessDataSource控件通过使用参数(Parameter)对象可以自定义SQL查询语句中所使用的筛选条件的值,并且很特别的是这些参数对象也可以从不同的来源那里获取参数值,其中包括了以下来源:QueryString查询字符串Session会话值,以及页面中其他WEB控件的值等等。参数对象的值也同连接字符串以及SELECT查询语句一样,可以通过声明方式来定义,这就意味着你可以无需为此编写一行代码。不过有些时候,如果你需要使用这些参数对象的值来控制程序的逻辑(比如你需要根据当前登录的用户名来进行逻辑判断。),那么这个时候这些参数对象的值同样可以通过编程的方式来设置。

 在本文中,我们将一起探索在SqlDataSource中,如何通过使用一个Parameter参数对象声明式地对SQL查询结果进行筛选。首先我们将了解使用硬编码的值进行筛选得的例子,接下来是通过QueryString以及其他Web控件动态获取筛选值得例子。

 数据筛选基础知识

当我们对结果集合进行筛选的时候,有2个值得我们所关注的因素:其一是被筛选的列,其二是做为筛选条件的值。在前言中,我们所使用的例子,UnitPrice就是被筛选的列,而15.00就是做为筛选条件的值。通常情况下,做为筛选条件的值都不是被硬编码的,而是依靠用户的输入动态决定的。在考虑到动态的筛选条件取值的情况后,SQL语句一般允许使用Parameters对象作为一个占位符来指示动态值所要插入的位置。Parameter参数通常以“@参数名”或者“?”的形式出现,至于实际使用哪种表示方法,则取决于所使用的数据库。比如,当使用动态参数对Access数据库进行查询时候,使用符号?。而对MSSQLServer进行查询时,则使用@参数名的表示方法。所以将我们在前言中所使用的例子更改后,使其符合动态输入的要求,如下:

-- For Microsoft SQL Server
SELECT ProductID, ProductName
FROM Products
WHERE UnitPrice < @PriceFilter 

-- For Microsoft Access
SELECT ProductID, ProductName
FROM Products
WHERE UnitPrice < ?  

(参数名称可以为任意值,在这里作者使用PriceFilter。)

指定好参数以后,就可以通过编程方式在执行SQL语句的时候为参数赋值。假如你很熟悉ADO.NET编程,你肯定知道我们如果要对一个数据库进行查询的话,需要首先创建一个Command命令对象(比如SqlCommand),然后指定其以普通SQL查询方式或者是存储过程方式执行(通过设置命令对象的CommandText属性),并且指定要使用的数据库连接对象(通过设置Connection属性的值)。其中,如果你观察的话就可以发现Command命令对象本身也包含了一个Parameters参数集合,这个集合允许你通过编程的方式指定在参数中所要使用的参数的名称和值(当然你使用存储过程的话,也可以设置参数的)。如果你要更多的了解如何对SQL Server数据库进行参数化查询,那么请看这个文章;如果你使用Access的话,那么请看这个文章,里面有关于参数化查询的介绍。

 不过如果你使用DataSource控件(下称数据源控件),你不需要通过编程方式来设置这些属性(除非你嗜好如此或者必需这么作),因为参数的值可以通过一种简洁的方式,被声明式地设置。不过如果你声明式地设置这些参数的值的时候,请确认你已经了解数据源控件行为,在你所看到的表象之下,它已经自动地通过编程的方式将你所指定的参数的值添加进Command命令对象的Parameters参数集合中去了。简而言之,数据源控件其实执行的代码是与你访问数据所编写代码是相同的。

 现在我们将注意力转回到使用SqlDataSource筛选数据上来。在下面的demo中(示例代码可以在文章最后的连接中下载到),我们将使用一个精简版本的Northwind Access数据库(我们在第2讲中讨论并使用过的)。我们将了解到如何通过SqlDatasource向导功能以及通过直接设置SqlDataSource属性来指定筛选参数。

 首先,创建一个ASP.NET页面然后转到设计视图。下一步,从工具箱中将一个SqlDataSource控件拖到页面上。正如我们在第2讲中所看的,你可以点击设计视图上SalDataSource控件的智能标记上的“配置数据源”设置将要连接到的数据库,并且还可以通过此设置执行查询的SQL语句。回忆以前所了解的,有2个不同的窗口可供我们来设置SELECT查询方式:

  • 指定来自表或视图的列(自动生成SQL语句) 你可以从一个下拉列表中选择某个数据表或数据视图,进而指定要返回的数据列。
  • 指定自定义SQL语句或存储过程使用这个选项你可以从一个下拉列表选择存储过程(或者使用查询语句生成器)。

如果你使用第一个选项“指定来自表或视图的列”,在选择要查询的数据表或视图,以及需要返回的列后,点击“WHERE…”按钮添加一个筛选参数。点击以后将弹出一个“添加WHERE子句”对话框(如下所视),在这个窗口里你可以指定需要被筛选的列,以及要使用的操作符(=<<=>,等等),以及做为筛选条件的值的来源(硬编码值,QueryString,或者是其他WEB控件等等)。我们将了解如何指定不同类型的参数源。

 

  

使用“添加WHERE子句”对话框的局限性

使用“指定来自表或视图的列”定义筛选有一个潜在的缺点 既是当你增加多个筛选参数时,这些筛选条件是通过“AND”操作符相连的。所以如果你需要靠多个子句来进行筛选并且希望子句之间通过OR连接的话(比如 SELECT * FROM Products WHERE UnitPrice < 15.00 OR UnitPrice > 25.00),那么你需要手动地增加SELECT查询语句以及WHERE子句,这个时候选择“指定自定义SQL语句或存储过程”这个选项就可以了。

 如果你使用“指定自定义SQL语句或存储过程”这个选项,你不但可以手动输入一个SQL语句,也可以从一个下拉列表中选择数据库中已有的存储过程。你只需要使用正确的语法就可以将参数方便地加入到一个SQL即席查询语句中去。因为我在此demo中所使用的数据库是微软的Access数据库,所以我相对应的就要使用“?”这个字符来代表一个参数变量的占位符。举个例子来说,假如如果我想使用一个SQL即席查询语句的话,我就得在SELECT标签下所对应的textbox文本框中输入以下语句:“SELECT ProdcutID,ProductName FROM Products WHERE UnitPrice < ?(如下图所示)。不过如果你正在使用的是一个SQL Server数据库系统得话,那就需要使用“@参数名”这样的语法来替换掉上文中的“?”符号啦。在你使用正确的语法指定了参数变量以后,单击下一步,屏幕上就会提示你指定参数变量的值了(以及参数的值的来源)。

 

第一步:设置参数化的即席查询的SQL语句(Ad-Hoc

 

第二步:设置参数的值(以及参数值的来源)

 当然,假如你正在使用的数据库系统是支持参数化的存储过程的(如微软的SQL Server数据库系统),那么你可以从下拉列表中选择一个存储过程并且跳过上文所说,直接单击下一步就Ok啦。在上图中所示的窗体里,你就可以看到你所选择的存储过程中所需要指定的参数的名称了,并且同样地你也可以指定这些参数的值以及是如何获得的。

 不管你使用什么方法,当你指定了参数的值以后,并且完成了SqlDataSource的向导过程以后,SqlDataSource控件将自动根据你的选择更新其属性。特别要指出的是,现在SelectCommand现在已经是一个参数化的查询了,并且将会创建许多SelectParameter的实例对象。因为SelectParameter的实例的类型是根据所获得的参数的值而动态改变的,所以在每个例子中,我们都要仔细检查SqlDataSource控件的标记的变化,以对声明性的标记有所了解。

 如果你反感使用向导的话,那么我建议你总是手动去输入参数化的查询语句以及SelectParameters(直接输入语法标记就是了),或者点击SqlDataSource属性面板中的SelectQuery属性来设置,在你点了SelectQuery属性以后,将会弹出一个窗体“命令和参数编辑器”,几乎相似的界面(如下图所示),你可以很容易上手。

  

 

为什么我们不使用FilterExpressionFilterParameters呢?

当你浏览SqlDataSource控件的属性列表时候,你会注意到有2个属性,一个是FilterExpression,另外一个属性是FilterParameters。你可能会诧异为什么我们不使用这两个属性呢,这个FilterParameters参数集合与SelectParameters参数集合有什么本质的区别呢?其实,这2个属性是为了筛选从数据库返回的结果集而设计的。也就是说,使用这两个属性的前提是数据已经从数据库返回,然后通过这2个参数来对返回的数据集进行筛选,最终供相关的WEB数据控件所使用的。换句话说,我们使用参数化的查询语句以及SelectParameters参数集合,是将筛选数据这一过程交由数据库端来完成的。

同样的你可能会猜测了,筛选交由数据库端处理是不是会比将数据全部返回数据源控件然后再筛选更有效呢。其实不然,我们一样有需要使用FilterExpression以及FilterParameters参数集合之时 实际上,这两种筛选技巧都可以被使用。回头来看我们的例子,使用的是参数化的查询语句以及SelectParameters参数集合,并且请牢记,SelectParameters指定的参数是通过参数化查询语句传递给数据库的参数,而FilterParameters参数集合则是对从数据库返回的数据进行筛选的。(并且只有当FilterExpression筛选表达式存在时才会设置此FilterParameters参数集合)。

 使用硬编码的值对数据进行筛选

无论上文中你选择“指定来自表或视图的列”或者是“指定自定义SQL语句或存储过程”,当你在设置参数的值得时候,都会被要求选择“参数源”,“参数源”是一组下拉列表中的任意一个选项。

  • None
  • Cookie
  • Control
  • Form
  • Profile
  • QueryString
  • Session

如果要指定硬编码的参数值,那么选择None就可以了(如同我们上文中所示),然后在DefaultValue那里输入一个硬编码的值。当你设置好了参数的值,并且完成了向导以后,查看下SqlDataSource控件的声明性标记语言应该如下所示:

 

<asp:SqlDataSource ID="ID" runat="server" ConnectionString="connectionString"
    ProviderName
="providerName"
    SelectCommand
="parameterizedQuery">
    
<SelectParameters>
        
<asp:Parameter DefaultValue="hardCodedValue" 
                       Name
="parameterName" 
                       Type
="dataType" />
    
</SelectParameters>
</asp:SqlDataSource>

 在这里特别要指出的是,在向导中你设置的每一个参数在声明性标记中都会有一个<asp:Parameter>标记与之对应,而其中DefaultValue属性则正代表了你刚才输入的硬编码的参数值,另外一个属性Name则代表了你添加的参数的名称(对于微软的Access数据库而言,属性Name的值应该与参数?占位符所代表的被筛选列的名称一致。唯一需要注意的一点就是<asp:Parameter>标记是与?相对应的。)。最后,Type属性是执行参数的数据类型。

 在本文最后共下载的demo文件中,你可以找到一个名字为“Filter on Hard-Coded Value Demo”的示例程序。在这个demo中,SqlDataSource控件被配置好从数据库中获取所有的UnitPrice < 15.00产品单价小于15.00Products产品数据。而最终此数据源的声明性标记代码如下所示,而最终显示在GridView数据控件中的数据,则如下图所示:

<asp:SqlDataSource ID="ID" runat="server" 
ConnectionString
="connectionString"
    ProviderName
="providerName"
    SelectCommand
="SELECT [ProductID], [ProductName], [UnitPrice] FROM [Products] WHERE ([UnitPrice] = ?)">
    
<SelectParameters>
        
<asp:Parameter DefaultValue="15.00" 
            Name
="UnitPrice" 
            Type
="Decimal" />
    
</SelectParameters>
</asp:SqlDataSource>
 

 

 

以某个指定的查询字符串QueryString的值做为数据筛选条件

打开Northwind数据库,查看表结构,我们会发现每个Product产品都是属于一个Category产品分类的。因此,我可能会需要一个页面来显示数据库中所有的产品分类,并且每个产品分类名称上都有一个超链接到另外一个页面(用来显示指定产品分类下的产品数据)。有一个方法可以实现这样的功能,那就是,首先创建2个页面:Categories.aspx,将所有的产品分类列出,还有一个页:ProductsInCategory.aspx,将一个产品分类下的所有产品列出。这项工作唯一的技术难点就在于如何设置,才能够在ProductsInCategory.aspx页中显示我们所选定的分类的产品数据。当然已知有许多种方法可以在同一站点的两个页面中传递数据,最常用的也就是QueryString了。那么使用这个方法的话,我们将使用下面这样的URL语句去访问ProductsInCategory.aspx?CategoryID=categoryID ,这样这个页面就会根据我们提供的查询字符串的值来显示指定分类下的产品信息了。(如果你还不了解什么是QueryString查询字符串么,那么不要紧张,你可以在这个连接Passing Parameters from One Page to Another所指向的文章中了解到以及如何编写代码来完成通过QueryString传递参数值的工作。)

 回到我们现在的工作中来,如果要使用一个QueryString来对数据进行筛选的话,你只需要在命令和参数编辑器或者向导中,将“数据源”那一项选择为QueryString即可。然后输入QueryString的名字并添加此参数。完成这项操作以后,你可以切换到代码视图,这个时候声明性标记应该如下所示:

<asp:SqlDataSource ID="ID" runat="server" ConnectionString="connectionString"
    ProviderName
="providerName"
    SelectCommand
="SELECT [ProductID], [ProductName], [UnitPrice] FROM [Products] WHERE ([CategoryID] = ?)">
    
<SelectParameters>
        
<asp:QueryStringParameter Type="Int32" 
            Name
="CategoryID" 
            QueryStringField
="CategoryID" />
    
</SelectParameters>
</asp:SqlDataSource>

注意:QueryStringParameter表示这个参数为QueryString类型的参数变量,而Name参数名称我们就设置为“CategoryID(产品分类ID),而QueryStringFiled则为我们需要传递的QueryString的变量名称。

 如下图所示,在Categories.aspx页面中,所有的产品分类都被列出,并且每个产品分类名称上都有一个超链接,而下面第2个图则是ProductsInCategory.aspx页面所显示的结果,正如我们所看到的一样,如果我们通过URLProductsInCategory.aspx?CategoryId=1来访问这个页面的话,就会将所有饮料信息都显示出来了。

 

所有产品的列表,每个产品名称上都有一个超链接。

 

 

饮料分类下的所有产品(也就是我们通过ProductsInCategory.aspx?CategoryId=1访问的结果)

 根据WEB控件传递的值来对数据进行筛选

当然SqlDataSource数据源控件所接受的值同样可以是页面上任何一个WEB控件所传递的值(某个WEB控件的某个属性)。对于我们上文所用的那个“Categories/Products master-detail”例子,有时候我们可能不想使用2个页面,而想使用一个页面来,这个页面上放一个下拉列表(里面显示所有的产品分类名称),然后我们从下拉列表中选择任意一个产品分类,就可以在下面的GridView控件中显示这个产品分类所对应的产品信息了。

为了实现这个功能,首先我们创建一个包含一个DropDownList的页面,为了让这个下拉列表控件显示所有产品分类信息,我们还要为其再单独创建一个SqlDataSource数据源控件,当我们所选择的下拉列表里的产品分类的值与数据表中的产品分类的值吻合的时候,就将显示该产品分类下的所有产品信息。在命令和参数编辑器或者向导中,参数源选择Control,然后从控件列表中选择刚才创建的DropDownList控件,就可以了。

 完成这几步后,SqlDataSource数据源控件的声明性标记应该如下所示了,

<asp:SqlDataSource ID="ID" runat="server" ConnectionString="connectionString"
    ProviderName
="providerName"
    SelectCommand
="SELECT [ProductID], [ProductName], [UnitPrice] FROM [Products] WHERE ([CategoryID] = ?)">
    
<SelectParameters>
        
<asp:ControlParameter Type="Int32" 
            Name
="CategoryID" 
            ControlID
="categoriesDDL"       
            PropertyName
="SelectedValue" />
    
</SelectParameters>
</asp:SqlDataSource>

 如下图所示,在这个页面中,最上面的DropDownList控件中列出所有的产品分类的名称,在下面的GridView控件中相应地显示出所选择的产品分类下的产品信息。注意:这个页面使用了2个SqlDataSource数据源控件 一个数据源控件用来获取所有的产品分类数据(并被绑定到DropDownList控件上),然后另外一个数据源控件则返回与所选产品分类相对应的产品数据(并被绑定到GridView控件上)。另外一点要注意的就是,我将DropDownList控件的AutoPostBack属性设置为True,这样只要DropDownList的选择被改变,那GridView控件也将立即更新相关的产品信息。

  

小结

在本文中,我们了解了使用SqlDataSource数据源控件的时候,如何使用参数化查询语句同参数集合对象配合来对从数据库返回的数据进行筛选。SqlDataSource数据源控件允许参数的值来自不同的数据源,包括了硬编码的,QueryString的,WEB控件的,Session会话的,等等。这些参数集合变量可以通过声明性编程方式指定,而无需编写一行代码。当然这些参数变量的值也同样可以通过编写程序代码来设置,我们将来未来的文章中对此作详细的阐述!

 Happy Programming!

· By Scott Mitchell


点下面的链接下载Demo示例程序

· Download the code used in this article

posted on 2006-11-29 09:16  戴南  阅读(1753)  评论(9编辑  收藏  举报