The web configuration file in Listing 31.11 includes an section that registers the LookupExpressionBuilder class for the prefix lookup.. When you use an ExpressionBuilder in a normal AS
Trang 1// Add Select with conString overload
AddSelectConString(dataType, xmlData);
// Create namespace
CodeNamespace dataNS = new CodeNamespace(“Data”);
// Add class to namespace
dataNS.Types.Add(dataType);
// Create code unit
CodeCompileUnit dataCode = new CodeCompileUnit();
// Add namespace to code unit
dataCode.Namespaces.Add(dataNS);
// Add default namespaces
dataNS.Imports.Add(new CodeNamespaceImport(“System”));
return dataCode;
}
}
}
The DataBuildProvider’s GenerateCode() method loads a data file into an XmlDocument
The VirtualPath property represents the path of the file being built For example, if you
add a file named Products.data to your project, the VirtualPath property would
repre-sent the path to the Products.data file
Next, the code for the data access component is created from the XML file by the
GetDataCode() method, which makes heavy use of the CodeDom to generate the code in a
language-neutral manner Working with the CodeDom is a strange and tedious experience
You must build up a block of code by building a code tree In Listing 31.9, a
CodeCompileUnit named dataCode is created A CodeNamespace named dataNS that
represents a namespace is created and added to the CodeCompileUnit And a
CodeTypeDeclaration named datatype that represents a class is added to the namespace
After the class is created, the methods and properties are added to the class block by block
An ExpressionBuilder class generates one expression from another expression Typically,
you use an ExpressionBuilder to look up a particular value given a particular key
The ASP.NET Framework includes the following ExpressionBuilder classes:
AppSettingsExpressionBuilder—Retrieves values from the appSettings section of
the web configuration file
Trang 2ConnectionStringsExpressionBuilder—Retrieves values from the
connectionStrings section of the web configuration file
ResourceExpressionBuilder—Retrieves values from resource files.
The ConnectionStringsExpressionBuilder has been used throughout this book whenever
a connection string has needed to be retrieved
You use the following syntax when working with an ExpressionBuilder:
<%$ ConnectionStrings:MyDatabase %>
The <%$ and %> tags mark an expression that should be parsed by an ExpressionBuilder
The prefix ConnectionStrings is mapped to the particular ExpressionBuilder class
responsible for parsing the expression ExpressionBuilders must always be used with
control properties For example, you cannot display a connection string in a page like this:
<%$ ConnectionStrings:MyDatabase %>
Instead, you must display the connection string like this:
<asp:Literal
Id=”ltlConnectionString”
Text=’<%$ ConnectionStrings:MyDatabase %>’
Runat=”server” />
You can create a custom ExpressionBuilder when none of the existing
ExpressionBuilder classes do what you need For example, you might want to store your
application settings in a custom section of the web configuration file In that case, you
might want to create a custom ExpressionBuilder that grabs values from the custom
configuration section
Creating a Lookup ExpressionBuilder
In this section, you learn how to extend ASP.NET Framework by building a custom
ExpressionBuilder class We create a Lookup ExpressionBuilder that looks up string
values from an XML file
The LookupExpressionBuilder class is contained in Listing 31.10
LISTING 31.10 App_Code\LookupExpressionBuilder.cs
using System;
using System.CodeDom;
using System.Web.UI;
using System.ComponentModel;
using System.Web.Compilation;
using System.Xml;
using System.Web.Hosting;
Trang 3using System.Web.Caching;
namespace AspNetUnleashed
{
public class LookupExpressionBuilder : ExpressionBuilder
{
public override CodeExpression GetCodeExpression(BoundPropertyEntry entry,
object parsedData, ExpressionBuilderContext context)
{
CodeTypeReferenceExpression refMe = new
CodeTypeReferenceExpression(base.GetType());
CodePrimitiveExpression expression = new
CodePrimitiveExpression(entry.Expression);
return new CodeMethodInvokeExpression(refMe, “GetEvalData”, new
CodeExpression[] { expression });
}
public override object EvaluateExpression(object target, BoundPropertyEntry
entry, object parsedData, ExpressionBuilderContext context)
{
return GetEvalData(entry.Expression);
}
public override bool SupportsEvaluate
{
get
{
return true;
}
}
public static string GetEvalData(string expression)
{
XmlDocument lookupDoc =(XmlDocument)HostingEnvironment.Cache[“Lookup”];
if (lookupDoc == null)
{
lookupDoc = new XmlDocument();
string lookupFileName = HostingEnvironment.MapPath(“~/Lookup
➥config”);
lookupDoc.Load(lookupFileName);
CacheDependency fileDepend = new CacheDependency(lookupFileName);
HostingEnvironment.Cache.Insert(“Lookup”, lookupDoc, fileDepend);
}
Trang 4string search = String.Format(“//add[@key=’{0}’]”, expression);
XmlNode match = lookupDoc.SelectSingleNode(search);
if (match != null)
return match.Attributes[“value”].Value;
return “[no match]”;
}
}
}
Before you can use the LookupExpressionBuilder class, you need to register it in the web
configuration file The web configuration file in Listing 31.11 includes an
<expressionBuilders> section that registers the LookupExpressionBuilder class for the
prefix lookup
LISTING 31.11 Web.Config
<?xml version=”1.0”?>
<configuration>
<system.web>
<compilation>
<expressionBuilders>
<add expressionPrefix=”lookup”
type=”AspNetUnleashed.LookupExpressionBuilder” />
</expressionBuilders>
</compilation>
</system.web>
</configuration>
The LookupExpressionBuilder uses an XML file named Lookup.config to contain a
data-base of lookup values This file contains key and value pairs A sample Lookup.config file
is contained in Listing 31.12
LISTING 31.12 Lookup.config
<?xml version=”1.0”?>
<lookup>
<add key=”WelcomeMessage” value=”Welcome to our Web site!” />
<add key=”Copyright” value=”All content copyrighted by the company.” />
</lookup>
Finally, the page in Listing 31.13 uses the LookupExpressionBuilder It contains a
Literal control that displays the value of a lookup expression named WelcomeMessage
(see Figure 31.3)
Trang 5FIGURE 31.3 Displaying text generated by an ExpressionBuilder
LISTING 31.13 ShowLookupExpressionBuilder.aspx
<%@ Page Language=”C#” %>
<!DOCTYPE html PUBLIC “-//W3C//DTD XHTML 1.0 Transitional//EN”
“http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd”>
<html xmlns=”http://www.w3.org/1999/xhtml” >
<head id=”Head1” runat=”server”>
<title>Show LookupExpressionBuilder</title>
</head>
<body>
<form id=”form1” runat=”server”>
<div>
<asp:Literal ID=”Literal1”
Text=”<%$ lookup:WelcomeMessage %>”
runat=”Server” />
</div>
</form>
</body>
</html>
Trang 6You create a custom ExpressionBuilder by inheriting a new class from the base
ExpressionBuilder class The ExpressionBuilder class has the following methods:
GetCodeExpression—Returns the code that evaluates an expression.
EvaluateExpression—Evaluates the expression in the case of no-compile ASP.NET
pages
ParseExpression—Returns a parsed version of the expression.
The ExpressionBuilder class also supports the following property:
SupportsEvaluate—When true, the ExpressionBuilder can be used in no-compile
ASP.NET pages
When you use an ExpressionBuilder in a normal ASP.NET page, the ExpressionBuilder
returns code that is integrated into the compiled ASP.NET page The GetCodeExpression()
method returns a block of code injected into the compiled ASP.NET page class that gets
created in the Temporary ASP.NET Files folder
Because an ExpressionBuilder might be used with either a Visual Basic NET or C#
ASP.NET page, the code returned by the GetCodeExpression() method must be language
neutral This means that you must represent the code that gets executed with the CodeDom
In Listing 31.11, the GetCodeExpression() method returns an instance of the
CodeMethodInvokeExpression class This class represents an expression that invokes a class
method In this case, the CodeMethodInvokeExpression class represents the expression
LookupExpressionBuilder.GetEvalData() In other words, the ExpressionBuilder adds
code to the compiled ASP.NET page class that invokes the GetEvalData() method
contained in Listing 31.10
As an alternative to creating a normal ASP.NET page, you can create something called a
no-compile ASP.NET page, which is not compiled dynamically You create a no-compile
ASP.NET page by adding the following attribute to a <%@ Page %> directive:
<%@ Page CompilationMode=”Never” %>
NOTE
No-compile ASP.NET pages are discussed in Chapter 1, “Overview of ASP.NET.”
If you want an ExpressionBuilder to work with no-compile ASP.NET pages, you must
return the value True from the ExpressionBuilder.SupportsEvaluate property and
imple-ment the EvaluateExpression() method The EvaluateExpression is executed at runtime
when the no-compile ASP.NET page is requested In Listing 31.11, the
EvaluateExpression() method simply calls the GetEvalData() method
Trang 7Creating HTTP Handlers
An HTTP Handler is a NET class that executes whenever you make a request for a file at a
certain path Each type of resource that you can request from an ASP.NET application has
a corresponding handler For example, when you request an ASP.NET page, the Page class
executes The Page class is actually an HTTP Handler because it implements the
IHttpHandler interface
Other examples of HTTP Handlers are the TraceHandler class, which displays
application-level trace information when you request the Trace.axd page, and the ForbiddenHandler
class, which displays an Access Forbidden message when you attempt to request source
code files from the browser
You can implement your own HTTP handlers For example, imagine that you want to
store all your images in a database table;however, you want use normal HTML <img> tags
to display images in your web pages In that case, you can map any file that has a gif or
.jpeg extension to a custom image HTTP handler The image HTTP handler can retrieve
images from a database automatically whenever an image request is made
Or imagine that you want to expose an RSS feed from your website In that case, you can
create an RSS HTTP Handler that displays a list of blog entries or articles hosted on your
website You can create an HTTP Handler in two ways You can either create something
called a Generic Handler, or you can implement the IHttpHandler interface in a custom
class This section explores both methods of creating an HTTP Handler
Creating a Generic Handler
The easiest way to create a new HTTP Handler is to create a Generic Handler When you
create a Generic Handler, you create a file that ends with the extension ashx Whenever
you request the ashx file, the Generic Handler executes
You can think of a Generic Handler as a lightweight ASP.NET page A Generic Handler is
like an ASP.NET page that contains a single method that renders content to the browser
You can’t add any controls declaratively to a Generic Handler A Generic Handler also
doesn’t support events such as the Page Load or Page PreRender events
In this section, we create a Generic Handler that dynamically generates an image from a
string of text For example, if you pass the string Hello World! to the handler, the
handler returns an image of the text Hello World!
The Generic Handler is contained in Listing 31.14
LISTING 31.14 ImageTextHandler.ashx
<%@ WebHandler Language=”C#” Class=”ImageTextHandler” %>
using System;
using System.Web;
using System.Drawing;
Trang 8using System.Drawing.Imaging;
public class ImageTextHandler : IHttpHandler
{
public void ProcessRequest(HttpContext context)
{
// Get parameters from querystring
string text = context.Request.QueryString[“text”];
string font = context.Request.QueryString[“font”];
string size = context.Request.QueryString[“size”];
// Create Font
Font fntText = new Font(font, float.Parse(size));
// Calculate image width and height
Bitmap bmp = new Bitmap(10, 10);
Graphics g = Graphics.FromImage(bmp);
SizeF bmpSize = g.MeasureString(text, fntText);
int width = (int)Math.Ceiling(bmpSize.Width);
int height = (int)Math.Ceiling(bmpSize.Height);
bmp = new Bitmap(bmp, width, height);
g.Dispose();
// Draw the text
g = Graphics.FromImage(bmp);
g.Clear(Color.White);
g.DrawString(text, fntText, Brushes.Black, new PointF(0, 0));
g.Dispose();
// Save bitmap to output stream
bmp.Save(context.Response.OutputStream, ImageFormat.Gif);
}
public bool IsReusable
{
get
{
return true;
}
}
}
Trang 9FIGURE 31.4 Displaying text images with an HTTP Handler
The ImageTextHandler in Listing 31.14 includes one method and one property The
ProcessRequest() method is responsible for outputting any content that the handler
renders to the browser
In Listing 31.14, the image text, font, and size are retrieved from query string fields You
specify the image that you want to return from the handler by making a request that
looks like this:
/ImageTextHandler.ashx?text=Hello&font=Arial&size=30
Next, a bitmap is created with the help of the classes from the System.Drawing namespace
The bitmap is actually created twice The first one measures the size of the bitmap required
for generating an image that contains the text Next, a new bitmap of the correct size is
created, and the text is drawn on the bitmap After the bitmap has been created, it is saved
to the HttpResponse object’s OutputStream so that it can be rendered to the browser
The handler in Listing 31.14 also includes an IsReusable property, which indicates
whether the same handler can be reused over multiple requests You can improve your
application’s performance by returning the value True Because the handler isn’t
maintain-ing any state information, there is nothmaintain-ing wrong with releasmaintain-ing it back into the pool so
that it can be used with a future request
The page in Listing 31.15 illustrates how you can use the ImageTextHandler.ashx file
This page contains three HTML <img> tags that pass different query strings to the handler
(see Figure 31.4)
Trang 10LISTING 31.15 ShowImageTextHandler.aspx
<%@ Page Language=”C#” %>
<!DOCTYPE html PUBLIC “-//W3C//DTD XHTML 1.0 Transitional//EN”
“http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd”>
<html xmlns=”http://www.w3.org/1999/xhtml” >
<head id=”Head1” runat=”server”>
<title>Show ImageTextHandler</title>
</head>
<body>
<form id=”form1” runat=”server”>
<div>
<img src=”ImageTextHandler.ashx?text=Some Text&font=WebDings&size=42” />
<br />
<img src=”ImageTextHandler.ashx?text=Some Text&font=Comic Sans MS&size=42” />
<br />
<img src=”ImageTextHandler.ashx?text=Some Text&font=Courier New&size=42” />
</div>
</form>
</body>
</html>
Implementing the IHttpHandler Interface
The big disadvantage of a Generic Handler is that you cannot map a Generic Handler to a
particular page path For example, you cannot execute a Generic Handler whenever
someone requests a file with the extension gif If you need more control over when an
HTTP Handler executes, you can create a class that implements the IHttpHandler interface
For example, the class in Listing 31.16 represents an Image HTTP Handler This handler
retrieves an image from a database table and renders the image to the browser
LISTING 31.16 App_Code\ImageHandler.cs
using System;
using System.Web;
using System.Data;
using System.Data.SqlClient;
using System.Web.Configuration;
namespace AspNetUnleashed
{