posts - 69, comments - 297, trackbacks - 21, articles - 0
  博客园 :: 首页 :: 新随笔 :: 联系 :: 订阅 订阅 :: 管理

SqlDataSource 執行資料篩選

Posted on 2008-06-11 21:32 jeff377 阅读(1260) 评论(8)  编辑 收藏 所属分类: ASP.NET 基础
摘要
使用 SqlDataSource 控件可以很方便的与 UI 控件 (如 GridView、FormView) 系结来呈现数据,若需要针对 SqlDataSource 做数据筛选时,最直觉的方式就是去修改 SqlDataSoruce.SelectCommand 的 SQL 命令来执行数据筛选,这样设定的呈现结果感觉是正确的,数据真得依设定的条件来筛选过滤。可以当 UI 控件重新做 DataBind 时,如 GridView 换页的动作,会发生数据又全部跑出来了。为何会有这样异常的结果呢?本文就是来说明发生这种情形的原因及正确的程序撰写方式。

SqlDataSource 执行数据筛选
在页面上放置 SqlDataSource 及 GridView 来呈现数据,并将 GridView 设定分页,页面控件的配置如下所示,当按下 [Filter] 按钮时,会依选取的字段名称及输入的筛选值来过滤数据。



在 [Filter] 按钮的 Click 事件撰写如下程序代码,主要是依设定的字段及筛选值产生 SQL 语法,并设定给  SqlDataSource.SelectCommand 属性。

    Protected Sub btnFilter_Click(ByVal sender As ObjectByVal e As System.EventArgs) Handles btnFilter.Click
        
Dim sSQL As String
        
Dim sFilter As String

        
Select Case ddlFile.SelectedValue.ToUpper
            
Case "EmployeeID".ToUpper
                sFilter 
= "[EmployeeID]=" & txtValue.Text
            
Case Else
                sFilter 
= "[" & ddlFile.SelectedValue & "] Like '%" & txtValue.Text & "%'"
        
End Select

        sSQL 
= "SELECT [EmployeeID], [LastName], [FirstName], [Title], [City] FROM [Employees] Where " & sFilter
        SqlDataSource1.SelectCommand 
= sSQL
    
End Sub

在 [DataBind] 按钮的 Click 事件撰写如下程序代码,主要是在显示 SqlDataSource.SelectCommand 属性值及执行 GridView.DataBind,使 GridView 重新系结数据。

    Protected Sub btnDataBind_Click(ByVal sender As ObjectByVal e As System.EventArgs) Handles btnDataBind.Click
        Response.Write(
"SelectCommand: " & SqlDataSource1.SelectCommand)
        GridView1.DataBind()
    
End Sub

执行程序,选择 EmployeeID 字段,输入筛选值为 "3",按下 [Filter] 按钮来执行筛选,执行结果可以正确筛选资料。



可是这时我们按下 [DataBind] 按钮,让 GridView 重新做数据系结。这时发生了奇怪的现象,资料怎么全又跑出来了呢?仔细看一下输出的 SqlDataSource.SelectCommand 属性值,它怎么不是我们刚刚设定的筛选 SQL 语法呢?若筛选的数据可以分页,你会发现按换页的结果,也会发生同样的情形,因为 换页也需要 DataBind,所以跟自行去设定 DataBind 的结果是一样的。



为什么设定的 SelectCommand 属性值不见了呢?主要原因就是 SelectCommand 属性并没有被保留在 ViewState 中,所以每次 PostBack 时,它的值就会还原为设计阶段的初始值。
 
SqlDataSource 执行数据筛选正确作法
即然我们知道原因是「SelectCommand 属性并没有被保留在 ViewState 中」,那最简单的方式就是我们自行撰写程序将其保留在 ViewState 中,故页面覆写 LoadViewState 及 SaveViewState 方法来保留 SelectCommand 属性。

    ''' <summary>
    
''' 由 ViewState 还原控件的状态。
    
''' </summary>
    
''' <param name="savedState">要还原的控件状态。</param>

    Protected Overrides Sub LoadViewState(ByVal savedState As Object)
        
If Not (savedState Is NothingThen
            
' Load State from the array of objects that was saved at ;
            ' SavedViewState.
            Dim myState As Object() = CType(savedState, Object())

            
If Not (myState(0Is NothingThen
                
MyBase.LoadViewState(myState(0))
            
End If

            
If Not (myState(1Is NothingThen
                SqlDataSource1.SelectCommand 
= CType(myState(1), String)
            
End If
        
End If
    
End Sub


    
''' <summary>
    
''' 控件的状态储存至 ViewState。
    
''' </summary>
    
''' <returns>含有控件之目前检视状态的对象。</returns>

    Protected Overrides Function SaveViewState() As Object
        
Dim baseState As Object = MyBase.SaveViewState()
        
Dim myState(1As Object
        myState(
0= baseState
        myState(
1= SqlDataSource1.SelectCommand
        
Return myState
    
End Function

重新执行程序,与之前的做法一样来筛选数据,最后按下 [DataBind] 按钮重新做数据系结,可以发现结果正确了,而输出的 SelectCommand 属性值也是我们最后设定的值。



Feedback

#1楼    回复  引用  查看    

2008-06-12 16:46 by 朝晖的.net      
mark......

#2楼    回复  引用    

2008-06-12 17:10 by wuya2 [未注册用户]
谢谢。
我以前的办法比较笨,当改变SqlDataSource的命令时,随便将SELECT命令保存到一个asp:HiddenField中,然后在重新加载页面的时候将隐藏控件的值再次赋予SqlDataSource.SelectCommand。

其实还有两个办法,可以酌情使用:
1.使用SqlDataSource.FilterExpression和SqlDataSource.FilterParameters对结果进行筛选;

2.在SqlDataSource中使用带参数的命令,然后再修改参数的值。
例如:SELECT命令为“SELECT * FROM TABLE1 WHERE [id]=@id”
SqlDataSource1.SelectParameters["id"].DefaultValue="xxx";

#3楼    回复  引用  查看    

2008-06-12 22:36 by 陈晨      
请教个问题:如果我在btnFilter_Click()事件中将SQL语句保存在ViewState,在Page_Load()事件中判断ViewState是否存在,如果存在的话赋值给SqlDataSource1.SelectCommand,这样可以吗?(我对页面的周期理解还很不够)

另外:个人觉得2楼的方法不错,利用了SqlDataSource控件的功能,这样配置好后,btnFilter_Click()事件中不用写任何代码,就可以实现数据筛选功能了。

#4楼 [楼主]   回复  引用  查看    

2008-06-12 23:22 by jeff377      
@陈晨
你要先了解ASP.NET 事件的顺序,Page Load 事件会比按钮的 Click 事件先发生,你的作法会发生先判断 ViewState设定 SelectCommand,后面才去设定 ViewState,会跟预期的执行顺序相反。
2楼的方法当然也可行,不过它只有限制筛选字段是固定的状况下才可以使用。

#5楼 [楼主]   回复  引用  查看    

2008-06-12 23:25 by jeff377      
@wuya2
设定 FilterExpression 属性的做法,它是针对 DataView 下 Filter,所以会先取回所有数据再针对 DataView 下 Filter;若数据非常多时(例如十万笔数据),而筛选符合的数据只有几笔,效能上会比较不好。

#6楼    回复  引用    

2008-07-10 09:42 by wythz [未注册用户]
2楼,我只使用了sqldatasource.FilterExpression来设置过滤参数,但是如果筛选后的结果大于1页的话,在分页时还是会将全部数据带出来

#7楼    回复  引用    

2008-07-14 23:32 by keanuzhang [未注册用户]
非常感谢,你的方法帮了我大忙,对比起来你的方法最优,其他方法在换页或者查询条件较复杂的时侯都会有问题。Thanks a lot!!!

#8楼    回复  引用  查看    

2008-08-30 10:34 by 陈晨      
--引用--------------------------------------------------
jeff377: @wuya2
<br>设定 FilterExpression 属性的做法,它是针对 DataView 下 Filter,所以会先取回所有数据再针对 DataView 下 Filter;若数据非常多时(例如十万笔数据),而筛选符合的数据只有几笔,效能上会比较不好。
--------------------------------------------------------
@wuya2的做法是效率不好

但楼主的这种做法在真正开发中也不可取,UI层夹杂了大量的SQL语句,为使程序很难维护。

比较好的做法是逻辑层封装数据库查询的过程,在UI层通过ObjcetDataSource调用逻辑层方法

标题  
姓名  
主页
Email (只有博主才能看到) 
验证码 *  看不清,换一张 [登录][注册]
内容(请不要发表任何与政治相关的内容)  
  登录  使用高级评论  新用户注册  返回页首  恢复上次提交      


相关链接: