CoreCoder
LANGUAGES: C#
ASP.NET
VERSIONS: 2.0
Express Yourself
Discern the Various Forms of Dynamic Expressions Available
in ASP.NET
By Dino Esposito
There are various flavors of dynamic expressions in
ASP.NET, each serving a particular scenario and providing a specific missing
capability. What are dynamic expressions? I m referring to any expression you
can write in the .aspx file wrapped by <% ... %> tags. As you ll see in a
moment, there are two types of expressions in ASP.NET 1.x. A third type of dynamic
expression has been added in ASP.NET 2.0. All of them, though, find inspiration
in the first form of dynamic expression that appeared with classic ASP quite a
few years ago: code blocks. In this article, I ll go through the three types of
expressions available in ASP.NET 2.0 and discuss their implications, as well as
some implementation details.
ASP-style Code Blocks
In classic ASP, code blocks are fragments of executable
code delimitated by <% ... %> tags. Within those tags, you can put
virtually everything that the ASP runtime engine can understand and parse,
ranging from variable assignments to for statements, from function declarations
to function calls. This apparently excessive liberty of coding is made possible
by the structure of the ASP page. The runtime engine builds an ASP page
incrementally as it parses the text. The final output is obtained by composing
any literal expression with the output of code blocks. A scripting environment
is up and running all the time to process the contents of <% ... %>
blocks statefully with respect to the current request. This explains why, say,
a function declared in one block can safely be invoked later in another block
the function body has been published and remains visible in the scripting
context.
There are two types of code blocks in classic ASP: inline
code and inline expressions. Inline expressions are merely shortcuts for
Response.Write and take the well-known form illustrated below:
<% x = 1 %>
<% = x %>
The former expression is an example of inline code; the
latter is an inline expression that outputs the contents of the x variable set one step earlier.
To the extent that it was made possible by a brand new run-time
environment, code blocks survived during the transition from classic ASP to
ASP.NET. They lost some features along the way, but still remain a supported
feature of the ASP.NET platform even today with version 2.0 just around the
corner.
In ASP.NET, code blocks can no longer be used to declare
functions, and variables are no longer considered globals. Inline statements
and expressions are accepted. Another difference that is worth noting is that
in ASP.NET, code blocks are processed at rendering time not when the page
loads up. This doesn t probably make a huge difference for old code, but it s
always the sign of an underlying brand new run-time model.
The contents of all code blocks are incorporated in the
form-rendering procedure and interspersed with calls to the RenderControl
method of declared server controls and literals. For this reason, an inline
expression can t be used to assign a property value in ASP.NET. In ASP, the
building model of the page made it possible because all text was accumulated
into a memory buffer and two successive writings could in the end compose an
attribute assignment. The object model of ASP.NET simply prohibits this
approach.
Data Binding Expressions in ASP.NET
To let developers assign dynamic values to control
attributes in a declarative manner, ASP.NET introduced in version 1.x a new
type of dynamic expression named data-bound expressions. At first sight,
data-bound expressions look similar to old-style code blocks. They look so
similar at the syntax level that one, with good reason, can wonder why two
types of expressions are necessary. Consider the following fragment:
<asp:Label id="Label1" runat="server"
Text='<%
DropDownList1.SelectedItem.Text %>' />
The Text property of the Label control is set to the text
of the currently selected item in a drop-down list control. More exactly, what
I described is only the desired behavior; unfortunately, it has nothing to do
with what happens in reality. Run the code and you ll see that the Text
property is assigned the literal expression, including the opening and closing
code block tags. You may think this is because of the quotes. Try removing the
quotes and run it again. This time you get a compile error informing you that <%
... %> expressions cannot be used that way in an ASP.NET page. This is where
data-bound expressions fit in. In terms of syntax, a data-bound expression is
nearly identical to a code block, except that it is prefixed by a # symbol. To
work, the preceding code label tag must be rewritten as shown here:
<asp:Label id="Label1" runat="server"
Text='<%#
DropDownList1.SelectedItem.Text %>' />
The simple insertion of a # symbol changes completely the
perspective of the markup. The ASP.NET page parser now creates an event handler
for the DataBinding event of that instance of the Label control, as the
pseudocode shown here documents:
Label1.DataBinding += new EventHandler(TextPropertyBinder);
Needless to say, the body of the TextPropertyBinder method
incorporates the evaluation of the expression and assigns it to the Text
property:
void TextPropertyBinder(object sender, EventArgs e)
{
Label1.Text =
DropDownList1.SelectedItem.Text;
}
The expression is copied verbatim; any error or wrong
syntax elements will be caught at compile time. It is important to note that
these expressions are evaluated only after a call to DataBind is made. The DataBind
method can be called on the page or the specific control. If no call is made,
no data-bound expressions are ever evaluated. You can use any code snippet in a
data-bound expression that returns a valid value for the property being bound.
You can even call methods on the Page class or external components.
What s wrong with data-bound expressions? Nothing, if
you re working with ASP.NET 1.x. But in ASP.NET 2.0, a new breed of controls
make their debut: data source controls. Data source controls manifest yet
another requirement where expressions are concerned. Heralding declarative
programming, data source controls should be bindable to the results of
expressions regardless of the binding mechanism. Data source controls operate
on a data source almost automatically, and fully support a scenario in which
all their settings are set at design time including how to cooperate with
other page controls to get required data. Data source controls are not
data-bound controls, even though they closely work with, and support,
data-bound controls (especially the new generation of data-bound controls such
as GridView and DetailsView). Not being data-bound controls, how can you
require that DataBind is called on source controls?
Note that DataBind is a method declared by the Control
class the root of all ASP.NET server controls and inherited by all controls,
including data source controls. In theory, you could use data-bound expressions
to configure data source controls. As long as you invoke DataBind on the data
source controls, it should work. Really? Well, not always. Let s see why. Try
the following code:
<asp:SqlDataSource id="MySource"
runat="server"
:
ConnectionString=<%
#ConfigurationManager.ConnectionStrings["LocalNWind"]
%>
<asp:GridView id="grid" runat="server"
datasourceid="MySource"
/>
You have a GridView control (the successor to the DataGrid)
bound to a SqlDataSource control. If you ve only absorbed 10% of the various
demonstrations, slides, sample code, and preview books on the topic of data
source controls, you should know by now what the code above does. For all the
others, I ll say that SqlDataSource takes a connection string and a bunch of
command strings (SELECT, plus optionally DELETE, INSERT, and UPDATE) and
provides fresh data to the bound control in this case, the grid.
How do you set the connection string? You can do that
programmatically in the Page_Load event. If this is fine for you, then there s
nothing more that I can add. For all the others, I ll say that you can also
think of setting the connection string declaratively by instructing the control
to retrieve the string programmatically. The code snippet above might work
(even with the extra burden of calling DataBind to trigger the process).
Now try swapping the position of the two controls in the
markup in such a way that the GridView control is processed before the SqlDataSource
control. When DataBind is called, all controls are bound to their data in the
order they appear in the page. Called to bind its data, the GridView control realizes
it depends on the SqlDataSource control. The DataSourceId property provides the
link. So the GridView control asks the SqlDataSource control to supply data.
Given the architecture of new data-bound controls bound to data source
controls, the GridView control tries to get its data by connecting to an
internal object inside the SqlDataSource and running the select command. Guess
what happens? The connection string property is not set at this time because
the data binding process for the SqlDataSource hasn t started yet.
Therefore, to fully support data source controls, a new
type of expression is required. ASP.NET 2.0 calls them $-expressions.
ASP.NET Dynamic Expressions
Use the following code to declaratively set the connection
string on a data source control:
<asp:SqlDataSource id="MySource"
runat="server"
:
ConnectionString=<%
$ConnectionStrings:LocalNWind %>
<asp:GridView id="grid" runat="server"
datasourceid="MySource"
/>
The $ symbol replaced the # symbol, thus giving life to a
brand new type of expression that requires a brand new type of support from the
ASP.NET runtime. But wait, there s more. As you can see, comparing the
expression above with the previous one, the contents of the <% ... %>
block is different. The call to a static method on the ConfigurationManager
class that you saw earlier is plain executable code. The expression you see
here, instead, takes a different form and is not directly executable:
<%$ [expressionPrefix]:[expressionValue] %>
Each $-expression must have a public prefix known by the
ASP.NET runtime, as well as a value. Each prefix is statically associated with
an expression builder class. The link takes place in the web.config file. The
value of the expression becomes input for some methods of the expression
builder class. The builder class ultimately determines the syntax of the value
expression. Before reviewing the default expression builders that ship with
ASP.NET 2.0, let me clarify that $-expressions are a parse-time feature. The
page parser is the only system tool that takes care of the expressions.
Whenever the page parser meets a $-expression, it extracts the prefix and,
through it, gets to the underlying expression builder class.
The expression builder class derives from a known base
class with a predefined interface; the page parser calls some methods and gets
the code to insert in the dynamically generated source for the requested ASPX
page. In the end, the expression above turns to the following:
MySource.ConnectionString = Convert.ToString(
ConnectionStringsExpressionBuilder.GetConnectionString(
"LocalNWind" ),
CultureInfo.CurrentCulture);
$-expressions are simply parse-time property setters.
Data-bound expressions are event-based and strictly tied to the classic data
binding process as defined in ASP.NET 1.x. They re similar, but not related.
Predefined $-expressions
As shown in Figure 1, there are three predefined
$-expressions in ASP.NET 2.0: AppSettings, ConnectionStrings, and Resources.
The AppSettings expression takes the name of an entry under the <appSettings>
section of the configuration file. ConnectionStrings is similar, except that it
reads the <connectionStrings> section. The value of the ConnectionStrings
expression can be in the form value.type. Here s an example:
<% $connectionstrings:northwind.connectionstring %>
<% $connectionstrings:northwind.providername %>
|
Prefix
|
Expression
|
Description
|
|
AppSettings
|
Entry
|
Retrieves the specified entry in the <appSettings>
section of the configuration file.
|
|
ConnectionStrings
|
Entry[.Type]
|
Retrieves the specified entry in the <connectionStrings>
section of the configuration file.
|
|
Resources
|
Class, ResourceName
|
Retrieves the specified resource in the given global
RESX file.
|
Figure 1: $-expressions
in ASP.NET 2.0.
Past the dot (.) symbol you can have any of the following:
connectionstring or providername. Basically, the string indicates the attribute
to retrieve in the specified entry. The .connectionstring is the default and
can be omitted. Here s how to use the ConnectionString expression to take
advantage of this feature:
<asp:sqldatasource runat="server"
id="SqlDataSource1"
ConnectionString='<% $
ConnectionStrings:NWind %>'
ProviderName='<% $
ConnectionStrings:NWind.providername %>'
:
/>
The Resources expression is used to access a global
resource declaratively. You need to specify two parameters, RESX file and
resource name. Here s an example:
<asp:label runat="server" id="Label1"
Text='<% $
Resources:Globals,HeaderString %>'
You cannot use the Resources expression to access resources
local to a page. Global resources are resources defined in an RESX file located
in the application s App_GlobalResources folder. $-expressions can also be
defined through the Visual Studio.NET 2005 designer. You select the control of
choice and click on the Expressions entry in the property grid. Once there, you
select the property you want to set and the expression you need, as shown in
Figure 2.
Figure 2: Setting expressions in
Visual Studio.NET 2005.
Conclusion
Even though ASP.NET 2.0 counts three different types of
expressions, it s difficult to say that any is redundant. Dynamic
$-expressions, added in ASP.NET 2.0, serve in particular to make more flexible
the declarative model of data source controls working in a way that
#-expressions cannot always do. Three predefined $-expressions make the
programming offer richer and provide a declarative way to bind resources and
configuration settings directly to control properties.
Dino Esposito is a
trainer and consultant who specializes in ASP.NET and ADO.NET. Author of Programming Microsoft ASP.NET and Introducing ASP.NET 2.0, both from Microsoft
Press, Dino also helped several companies architect and build effective
products for ASP.NET developers. Dino is the cofounder of http://www.DotNet2TheMax.com, a
popular portal for .NET programmers. Write to him at mailto:dinoesp@hotmail.com or join the
blog at http://weblogs.asp.net/despos.