CHAPTER 16 Using the QueryExtender Control And you might define a static method in the page though we recommend explicitly defining the type name so you can avoid filling your code-behin
Trang 1CHAPTER 16 Using the QueryExtender Control
And you might define a static method in the page (though we recommend explicitly
defining the type name so you can avoid filling your code-behinds with additional logic)
as follows:
public static IQueryable<Product>
FilterOutPastDueAccounts(IQueryable<BankAccount> query)
{
return from account in query
where account.Status != AccountStatuses.PastDue select account;
}
The return value from this method is passed down the chain of filter expressions One
great advantage of putting code behind these expressions is that they can be easily reused
across multiple pages (This is another reason we recommended specifying the type name
of the expression class explicitly rather than putting the expression method in the page’s
code-behind.)
Sorting with the OrderByExpression
The OrderByExpression provides developers with a way of specifying a sort expression
that can be applied to the query within the query extender control The two properties
of the OrderByExpression that control the sorting are the DataField and Direction
properties
The DataField property contains the name of the column on which the sorting will take
place, and the Direction property indicates which direction the sorting will be done
<asp:QueryExtender runat=”server” TargetControlID=”AccountsReceivableSource”>
<asp:OrderByExpression DataField=”AmountDue” Direction=”Descending”/>
</asp:QueryExtender>
In the preceding code we sort the accounts receivable rows in descending order by the
amount due This should give us a nice list of the people who owe us money and where
they live This might not be sufficient What if, for example, for each dollar amount, we
want to sort the receivables by the age of the customer? This can enable us to see the
oldest person that owes us each amount of money We can use a ThenBy tag as a child of
the OrderByExpression to accomplish this as shown below:
<asp:QueryExtender runat=”server” TargetControlID=”AccountsReceivableSource”>
<asp:OrderByExpression DataField=”AmountDue” Direction=”Descending”>
<asp:ThenBy DataField=”CustomerAge” Direction=”Descending”/>
</asp:QueryExtender>
Trang 2Querying with the PropertyExpression
The PropertyExpression enables the developer to use the query extender control to filter
the result set based on the values of specific columns Remember that the property
expres-sion works only with equality For example, you cannot use a property expresexpres-sion to filter
where the value contained in a column is less than or greater than some other value
The following example shows how to filter a data source of ransom demands in which the
ransom value is selected from a drop-down box:
<asp:DropDownList AutoPostBack=”true” ID=”RansomValues” runat=”server”>
<asp:ListItem Value=”1000000”>1 Meeeeelion Dollars!</asp:ListItem>
<asp:ListItem Value=”1”>1 dollar</asp:ListItem>
</asp:DropDownList>
<asp:QueryExtender runat=”server” TargetControlID=”RansomDataSource”>
<asp:PropertyExpression>
<asp:ControlParameter ControlID=”RansomValues” Name=”RansomValue”/>
</asp:PropertyExpression>
</asp:QueryExtender>
There’s actually quite a bit of interesting functionality in the preceding code The first
thing you see is that we have a drop-down list containing a list of possible filter values for
ransom demands In this code sample, we hardcoded the values but those could easily be
data bound Next is the query extender, extending a data source called RansomDataSource
The query in that data source is filtered to contain only those rows whose RansomValue
column is exactly equal to the currently selected value of the RansomValues drop-down
list This means that if the user were to select the first value in the drop-down list, the
page automatically posts back, and the data source automatically filters itself based on the
new ransom value
Control Parameters enable us to continue to use declarative query extension at the same
time as pulling valuable information from other controls on the page rather than
poten-tially complicated and messy code-behind classes
Querying with the RangeExpression
If the filter you’re looking to apply can be expressed as a range expression, this control
can do the trick A range expression is an expression in which you test whether a value is
less than or greater than a target value or falls within a target range This is a versatile
expression, as shown in the following sample that filters a data source for rows in which
the transaction posting date occurred within the two date periods chosen by the user with
two text boxes that have jQuery-based calendar pop-ups feeding them (jQuery code is not
included here because it’s beyond the scope for this chapter):
Trang 3CHAPTER 16 Using the QueryExtender Control
<asp:QueryExtender runat=”server” TargetControlID=”AccountsDataSource”>
<asp:RangeExpression DataField=”TransactionPostDate” MinType=”Inclusive”
MaxType=”Inclusive”>
<asp:ControlParameter ControlID=”FromDate” />
<asp:ControlParameter ControlID=”ToDate” />
</asp:RangeExpression>
</asp:QueryExtender>
Querying with the SearchExpression
The search expression functionality of the query extender is one of the most powerful
expressions available It enables you to specify a field or list of fields and search those
fields by doing a comparison against a search string This search can be a “starts with”,
“contains”, or “ends with” search Gone are the days in which the developer has to
hand-write these kinds of queries, which is typically an arduous, error-prone process This
expression requires you to specify the SearchType and DataFields properties, but it does
the rest of the magic for you Optionally, you can use the ComparsionType to control the
case-sensitivity of the search (depending on whether the underlying data source you used
supports this functionality)
As with all these expressions, you can combine them with other expressions to create rich
queries without having to drop into your code-behind to do manual sorting and filtering
The following sample illustrates how to use the search expression to provide a
full-featured search page (although a real-world, production search page would probably have
a better UI!)
<form id=”searchForm” runat=”server”>
Query: <asp:TextBox ID=”SearchQueryTextBox” runat=”server”/><br/>
<asp:Button ID=”searchButton” runat=”server” Text=”Search” />
<asp:LinqDataSource ID=”ContactsDataSource”
ContextTypeName=”MyDatabaseDataContext”
TableName=”Contacts” runat=”server” />
<asp:QueryExtender runat=”server” TargetControlID=”ContactsDataSource”>
<asp:SearchExpression SearchType=”Contains”
DataFields=”FirstName,LastName,UserName”>
<asp:ControlParameter ControlID=”SearchQueryTextBox” />
</asp:SearchExpression>
</asp:QueryExtender>
<asp:GridView ID=”ContactsGrid” runat=”server”
DataSourceID=”ContactsDataSource”>
</asp:GridView>
</form>
Trang 4By now most of the markup in the preceding code sample should look familiar; however
no code-behind is driving the actual querying of the database The query extender is
auto-matically going to modify the query (if there is anything in the search box) and filter the
results every time a postback occurs The button press causes a postback but no code in
the code-behind needs to be executed because all the search logic has been rigged up
declaratively in the aspx page
Whether having this “querying as a side-effect” is a good thing is a debate that will
proba-bly rage on for quite some time as many developers prefer to explicitly control when and
how the query is performed, and obviously proponents of ASP.NET MVC Framework
would have the query instigated in a controller action method rather than declaratively in
the markup
Building a Sample Page with the QueryExtender
Control
Now that we’ve seen all the different options available to us when using the
QueryExtender control, let’s put this all together with a real data source and some controls
on the page and see how it all works as a unit
In this sample, we point a LINQ data source (a data source for LINQ to SQL contexts) at a
context that contains tables for a video game catalog database When the data source is set
up, we just need to create an ASP.NET Web Form page called GameBrowser.aspx This page
needs to have a drop-down list that enables users to filter games by Genre and a text box
that enables users to search both game title and game description Take a look at the
markup for the GameBrowser.aspx page:
<form id=”form1” runat=”server”>
<div>
Genre: <asp:DropDownList runat=”server” ID=”GenreDropDownList”
DataSourceID=”GenreSource” DataTextField=”Name”
DataValueField=”ID” AutoPostBack=”true” /><br />
Game Search: <asp:TextBox ID=”GameSearchTextBox” runat=”server” /><br />
<asp:GridView runat=”server” ID=”GamesGrid” DataSourceID=”GamesSource”
AutoGenerateColumns=”False” BackColor=”LightGoldenrodYellow”
BorderColor=”Tan” BorderWidth=”1px” CellPadding=”2” ForeColor=”Black”
GridLines=”None”>
<AlternatingRowStyle BackColor=”PaleGoldenrod” />
<Columns>
<asp:BoundField DataField=”ID” HeaderText=”ID” ReadOnly=”True”
SortExpression=”ID” />
<asp:BoundField DataField=”Title” HeaderText=”Title”
➥ReadOnly=”True”
SortExpression=”Title” />
Trang 5CHAPTER 16 Using the QueryExtender Control
ReadOnly=”True” SortExpression=”Description” />
<asp:BoundField DataField=”GenreID” HeaderText=”GenreID”
➥ReadOnly=”True”
SortExpression=”GenreID” />
<asp:BoundField DataField=”GenreName” HeaderText=”Genre Name” Read
➥Only=”true” />
<asp:BoundField DataField=”ESRB” HeaderText=”ESRB” ReadOnly=”True”
SortExpression=”ESRB” />
</Columns>
<FooterStyle BackColor=”Tan” />
<HeaderStyle BackColor=”Tan” Font-Bold=”True” />
<PagerStyle BackColor=”PaleGoldenrod” ForeColor=”DarkSlateBlue”
HorizontalAlign=”Center” />
<SelectedRowStyle BackColor=”DarkSlateBlue” ForeColor=”GhostWhite” />
<SortedAscendingCellStyle BackColor=”#FAFAE7” />
<SortedAscendingHeaderStyle BackColor=”#DAC09E” />
<SortedDescendingCellStyle BackColor=”#E1DB9C” />
<SortedDescendingHeaderStyle BackColor=”#C2A47B” />
</asp:GridView>
<asp:LinqDataSource ID=”GamesSource” runat=”server”
ContextTypeName=”QueryExtenderApplication.VideoGamesModelDataContext”
TableName=”VideoGames” EntityTypeName=””
Select=”new (Genre.Name as GenreName, ID, Title, Description, GenreID,
➥ESRB)” />
<asp:LinqDataSource ID=”GenreSource” runat=”server”
ContextTypeName=”QueryExtenderApplication.VideoGamesModelDataContext”
TableName=”Genres” EntityTypeName=”” />
<asp:QueryExtender runat=”server” TargetControlID=”GamesSource”>
<asp:PropertyExpression>
<asp:ControlParameter ControlID=”GenreDropDownList” Name=”GenreID” />
</asp:PropertyExpression>
<asp:SearchExpression ComparisonType=”InvariantCultureIgnoreCase”
DataFields=”Title,Description” SearchType=”Contains”>
<asp:ControlParameter ControlID=”GameSearchTextBox” />
</asp:SearchExpression>
</asp:QueryExtender>
</div>
</form>
Trang 6Aside from the few bits of styling information that came from choosing Autoformat on
the GridView, the preceding code isn’t all that complex The first thing we do is create a
drop-down list bound to the list of genres in the video games catalog Second, we create a
text box that will eventually filter the list of games
After the GridView, there are two LINQ data sources The first data source points to the
Genre table in the video game LINQ to SQL model The second points to the VideoGames
table; you might notice that we’re actually including a foreign key property (Genre.Name)
and adding that to the anonymous type returned by the query
Finally, we get to the query extender The first part of the query extender is a
PropertyExpression that we use to filter the GenreID property based on the currently
selected value of the GenreDropDownList control When you try this page (after creating a
bogus video game catalog), you see that the form automatically reposts and requeries
every time you select a new genre Obviously you can change this behavior for a
produc-tion applicaproduc-tion, but it helps drive home the point that all the QueryExtender
functional-ity can be contained solely in the aspx markup
The second part of the query extender is a SearchExpression Here we’re further filtering
the result set by searching the Title and Description columns of the VideoGame table
for a substring indicated by the current value of the GameSearchTextBox control
Figure 16.1 shows the output of the page after selecting a genre from the drop-down box
Figure 16.2 shows the output of the same page after supplying some text on which to
filter the games list Note that there’s no button on this page, just hitting enter within the
text box will cause a re-query after a postback
FIGURE 16.1 Filtering using the QueryExtender
Trang 7CHAPTER 16 Using the QueryExtender Control
Summary
This chapter provided you with an overview of building data-driven pages in which the
filtering and sorting logic was embedded directly in the page in a declarative fashion using
the QueryExtender control and its associated expressions
By enabling you to define your queries and sort expressions declaratively in the markup,
the QueryExtender also enables you to make those queries dynamic by pulling in values
from other controls on the page at runtime This makes building user-driven forms that
perform searches, queries, and sorts incredibly easy
The QueryExtender control is just one more tool for the ASP.NET developer’s arsenal for
rapidly building powerful, data-driven Web Forms
FIGURE 16.2 Filtering using the QueryExtender and a SearchExpression
Trang 8Building Components .. Building Basic ComponentsBuilding Component Libraries
Architectural Considerations
Summary
Components enable you to reuse application logic across
multiple pages or even across multiple applications For
example, you can write a method named GetProducts()
once and use the method in all the pages in your website
By taking advantage of components, you can make your
applications easier to maintain and extend
For simple applications, you do not need to take advantage
of components; however, as soon as your application
contains more than a few pages, you’ll discover that you are
repeating the same work over and over again Whenever
you discover that you need to write the same method more
than once, you should immediately rip the method out of
your page and add the method to a component
In this chapter, you learn how to build components in NET
Framework First, you get an overview of writing
compo-nents: You learn how to create simple components and use
them in the pages in your application In particular, you
learn how to define component methods, properties, and
constructors You also learn how to take advantage of
over-loading, inheritance, and interfaces
Next, you learn how to build component libraries that can
be shared across multiple applications You examine
differ-ent methods of compiling a set of compondiffer-ents into
assem-blies You also learn how you can add a component library
to the Global Assembly Cache
Finally, we discuss architectural issues involved in using
components The final section of this chapter shows you
how to build a simple three-tiered application that is
divided into distinct User Interface, Business Logic, and
Data Access layers
Trang 9CHAPTER 17 Building Components
NOTE
Let’s clarify the terminology In this book, I use the word component as a synonym for
the word class Furthermore, by the word object, I mean an instance of a class.
I am ignoring a special meaning for the word component in NET Framework
Technically, a component is a class that implements the
System.ComponentModel.IComponent interface I am ignoring this special meaning of
the word component in favor of the common language use of the word.
Building Basic Components
Let’s start by building a super simple component The HelloWorld component is contained
in Listing 17.1
LISTING 17.1 HelloWorld.cs
public class HelloWorld
{
public string SayMessage()
{
return “Hello World!”;
}
}
VISUAL WEB DEVELOPER NOTE
When using Visual Web Developer, you create a component by selecting Website, Add
New Item, and then selecting the Class item (see Figure 17.1) The first time you add a
component to a project, Visual Web Developer prompts you to create a new folder
named App_Code You want your new component to be added to this folder
The HelloWorld component consists of a single method named SayMessage() that returns
the string Hello World!
Make sure that you save the HelloWorld.cs file to your application’s App_Code folder If
you don’t save the component to this folder, you can’t use the component in your pages
Next, you need to create a page that uses the new component This page is contained in
Listing 17.2
Trang 10FIGURE 17.1 Creating a new component with Visual Web Developer
LISTING 17.2 ShowHelloWorld.aspx
<%@ Page Language=”C#” %>
<!DOCTYPE html PUBLIC “-//W3C//DTD XHTML 1.1//EN”
“http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd”>
<script runat=”server”>
void Page_Load()
{
HelloWorld objHelloWorld = new HelloWorld();
lblMessage.Text = objHelloWorld.SayMessage();
}
</script>
<html xmlns=”http://www.w3.org/1999/xhtml” >
<head id=”Head1” runat=”server”>
<title>Show Hello World</title>
</head>
<body>
<form id=”form1” runat=”server”>
<div>
<asp:Label
id=”lblMessage”