Skip to content
February 11, 2011 / pauldundon

Creating and Modifying QueryExtenders in code

The Problem: You need to be able to create a QueryExtender on the fly, or modify the query it performs in code. You attempt to create a QueryExtender object and add it to your page during, eg, OnLoad, but find that it no longer modifies the data returned from the data source.

The Cause: QueryExtender works by creating a chain of IQueryable objects which filter the data from the data source. This chain is installed in the QueryCreated event of the data source. The QueryExtender registers its handler for this event during its OnInit. This means that if the QueryExtender is added to the page at any point after OnInit, it will not add the handler, and so will not install the processing chain.

Rejected solutions: It is in principle possible to invoke OnInit at the point where you add the QueryExtender to the page, either through reflection or by deriving a class from QueryExtender and adding a public method which invokes the protected OnInit method. This approach is likely to work, but is fragile; changes to the implementation of QueryExtender are likely to break it.

Possible approaches: The best approach depends on whether it is possible to create the QueryExtender itself in or before the page Init event, either by having it as part of the aspx file, or by creating it during the page OnInit or Page_Init event handler. If this is possible, the QueryExtender will initialise correctly and you can add expressions (like SearchExpression) in code later in the page lifecycle.

    Dim qx1 As QueryExtender = Form.FindControl("qx1")
    Dim se As New SearchExpression
    se.SearchType = SearchType.Contains
    qx1.Expressions.Add(se)
    se.DataFields = "Email"
    Dim prm As New ControlParameter
    prm.ControlID = "txtSearch"
    se.Parameters.Add(prm)

Although the QueryExtender installs the event handler during Init, the expressions aren’t evaluated until DataBind, so adding them late isn’t a problem so long as DataBind gets called afterwards.

If you have to create a QueryExtender later in the page lifecycle – for example, if you’re using it as part of a composite control – there is no satisfactory solution. However, QueryExtender itself does very little work which isn’t a consequence of it being used declaratively: the heavy lifting is done by the expressions (like SearchExpression). It’s straightforward to use these in code; actually there is slightly less code involved than using QueryExtender:

    Protected Sub QueryCreatedHandler(ByVal sender As Object, 
                             ByVal e As QueryCreatedEventArgs)
        Dim ex As New SearchExpression
        ex.ComparisonType = StringComparison.CurrentCultureIgnoreCase
        ex.SearchType = SearchType.Contains
        ex.DataFields = "Email"

        Dim prm As New ControlParameter
        prm.ControlID = "txtSearch"
        ex.Parameters.Add(prm)
        ' In this example, the text box txtSearch is hosted in a panel which is
        ' the first control in our control collection. Thus, Me.Controls(0) is
        ' the panel containing txtSearch. The following call to SetContext
        ' means that when the parameter
        ' object calls FindControl, it is able to find the textbox
        ex.SetContext(Me.Controls(0), Context, DataSource)
        Dim query As IQueryable = ex.GetQueryable(e.Query)
        If Not query Is Nothing Then
            e.Query = query
        End If
    End Sub

This code assumes that you add QueryCreatedHandler as a handler for the data source QueryCreated event. The call to SetContext on the expression object provides it with the information it will need to resolve the parameter values. In the example, DataSource is implemented elsewhere as a property returning the data source component.

The call setting e.Query installs the expression object as part of the processing chain. You can repeat this line of code for each expression you want to install. If SearchExpression finds itself with nothing to do (eg, the text box is empty) it will return Nothing from GetQueryable, so it’s necessary to test for this. If we don’t, and set e.Query to Nothing, the whole processing chain will be lost but the framework won’t raise any error.

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: