Faqs
Faqs
Faqs
Currently, it's supported on Windows 2000 and Windows XP. ASP.NET integrates with Internet Information Server (IIS) and thus requires that IIS be installed. It runs on server and nonserver editions of Windows 2000 and XP as long as IIS is installed. Microsoft originally planned to support ASP.NET on Windows NT 4.0, but had to reconsider due to time and technical constraints.
Top
Top
Why can't I put <%@ Page Language="C# " %> at the top of an ASPX file and write my server-side scripts in C# ?
Because the parsers ASP.NET uses to extract code from ASPX files only understand C#, Visual Basic.NET, and JScript.NET. However, if you use code-behind to get your code out of the ASPX file and into a separately compiled source code file. You can write server-side scripts in any language supported by a .NET compiler.
Top
<%@ Import Namespace="System.Data" %> <script language="C#" runat="server"> void Application_Start () { DataSet ds = new DataSet (); ds.ReadXml (Server.MapPath ("GlobalData.xml")); Application["GlobalData"] = ds; } </script>
using System.Web; using System.Data; public class MyApp : HttpApplication { public void Application_Start () { DataSet ds = new DataSet (); ds.ReadXml ("GlobalData.xml"); Application["GlobalData"] = ds; } }
So that ASP.NET can find the MyApp class, compile it into a DLL (csc filename.cs) and place it in the application root's bin subdirectory.
/t:library
Top
Can you override method="post" in a <form runat="server"> tag by writing <form method="get" runat="server">?
Yes.
Top
Can an ASPX file contain more than one form marked runat="server"?
No.
Top
Is it possible to see the code that ASP.NET generates from an ASPX file?
Yes. Enable debugging by including a <%@ Page Debug="true" %> directive in the ASPX file or a <compilation debug="true"> statement in Web.config. Then look for the generated CS or VB file in a subdirectory underneath \%SystemRoot %\Microsoft.NET\Framework\v1.0.nnnn\Temporary ASP.NET Files.
Top
Managed types created this way are identified by class name. Unmanaged types (COM classes) are identified by CLSID or ProgID.
Top
Top
How do I debug an ASP.NET application that wasn't written with Visual Studio.NET and that doesn't use code-behind?
Start the DbgClr debugger that comes with the .NET Framework SDK, open the file containing the code you want to debug, and set your breakpoints. Start the ASP.NET application. Go back to DbgClr, choose Debug Processes from the Tools menu, and select aspnet_wp.exe from the list of processes. (If aspnet_wp.exe doesn't appear in the list, check the "Show system processes" box.) Click the Attach button to attach to aspnet_wp.exe and begin debugging. Be sure to enable debugging in the ASPX file before debugging it with DbgClr. You can enable tell ASP.NET to build debug executables by placing a
Top
Application_Start Application_End
Session_Start Session_End
Application_BeginRequest Application_AuthenticateRequest Application_AuthorizeRequest Application_ResolveRequestCache Application_AcquireRequestState Application_PreRequestHandlerExecute Application_PostRequestHandlerExecute Application_ReleaseRequestState Application_UpdateRequestCache Application_EndRequest
Application_Error Application_Disposed
Global.asax can also include handlers for events fired by custom HTTP modules. The event handlers listed above are intrinsic to ASP.NET.
Top
Is it possible to protect view state from tampering when it's passed over an unencrypted channel?
Yes. Simply include an @ Page directive with an EnableViewStateMac="true" attribute in each ASPX file you wish to protect, or include the following statement in Web.config:
This configuration directive appends a hash (officially called the message authentication code, or MAC) to view state values round-tripped to the client and enables ASP.NET to detect altered view state. If ASP.NET determines that view state has been altered when a page posts back to the server, it throws an exception. The hash is generated by appending a secret key (the validationKey value attached to the <machineKey> element in Machine.config) to the view state and hashing the result. An attacker can't modify view state and fix up the hash without knowing the secret key, too.
Top
Is it possible to encrypt view state when it's passed over an unencrypted channel?
Yes. Set EnableViewStateMac to true and either modify the <machineKey> element in Machine.config to look like this:
<add verb="*" path="*.asax" type="System.Web.HttpForbiddenHandler, ... /> <add verb="*" path="*.config" type="System.Web.HttpForbiddenHandler, ... />
Top
<html> <head> <style> .Input { font: 10pt verdana; color: red; } </style> </head> <body> <form runat="server"> <asp:TextBox CssClass="Input" RunAt="server" /> </form> </body> </html>
Top
Top
Top
System System.Collections System.Collections.Specialized System.Configuration System.Text System.Text.RegularExpressions System.Web System.Web.Caching System.Web.Security System.Web.SessionState System.Web.UI System.Web.UI.HtmlControls System.Web.UI.WebControls
What assemblies can I reference in an ASPX file without using @ Assembly directives?
ASP.NET links to the following assemblies by default:
This list of "default" assemblies is defined in the <assemblies> section of Machine.config. You can
Top
How does setting a Web control's AutoPostBack property to true cause a page to post back to the server?
With a sprinkle of JavaScript and a dash of Dynamic HTML (DHTML). Enter this into a Web form:
<input name="UserName" type="text" id="UserName" onchange="__doPostBack('UserName','')" language="javascript" /> . . . <script language="javascript"> <!-function __doPostBack(eventTarget, eventArgument) { var theform = document.ctrl0; . . . theform.submit(); } // --> </script>
The <input> tag includes an onchange attribute that activates a JavaScript function named __doPostBack on the client when the control loses the input focus following a text change. __doPostBack programmatically posts the page back to the server by calling the Submit method of the DHTML object that represents the form (theform).
Top
I sometimes see ASP.NET apps that include ASHX files. What are ASHX files?
ASHX files contain HTTP handlers-software modules that handle raw HTTP requests received by ASP.NET. The following code institutes a simple HTTP handler:
<%@ WebHandler Language="C#" Class="Hello"%> using System.Web; public class Hello : IHttpHandler { public void ProcessRequest (HttpContext context)
{ }
If this code is placed in an ASHX file named Hello.ashx and requested using the URL http://.../hello.ashx?Name=Jeff, it returns "Hello, Jeff" in the HTTP response. ASHX files provide developers with a convenient way to deploy HTTP handlers without customizing CONFIG files or modifying the IIS metabase.
Top
using System; using System.Web; using System.Web.UI; namespace Wintellect { public class HelloControl : Control { protected override void Render (HtmlTextWriter writer) { writer.Write ("Hello, World!"); } } }
A custom control emits HTML by overriding the virtual Render method it inherits from Control and using the provided HtmlTextWriter to write its output.
Top
What does the System.Web.UI.Page.RegisterClientScriptBlock method do, and do I need it when I write custom ASP.NET server controls?
RegisterClientScriptBlock enables a custom control to register a block of client-side script that the control returns to a browser. Why does it exist? So the same script block doesn't get returned multiple times if the page contains multiple instances of a control that emits clientside script. Here's the source code for a custom control called AlertButton that renders itself
an as <input type="submit"> tag with an onclick attribute that displays a message using a JavaScript alert:
namespace Wintellect { public class AlertButton : Control { protected string _Text; protected string _Message; public string Text { get { return _Text; } set { _Text = value; } } public string Message { get { return _Message; } set { _Message = value; } } protected override void OnPreRender (EventArgs e) { Page.RegisterClientScriptBlock ( "__doAlert", "<script language=\"javascript\">\n" "<!--\n" "function __doAlert (message)\n" "{\n" " alert (message);\n" "}\n" "-->\n" "</script>" ); } protected override void Render (HtmlTextWriter writer) { StringBuilder builder = new StringBuilder (); builder.Append builder.Append builder.Append builder.Append builder.Append } ("<input type=\"submit\" value=\""); (_Text); ("\" onclick=\"javascript:__doAlert (\'"); (Message); ("\');\" />");
If the control's register tag prefix is win, then the following statement declares an AlertButton control that, when clicked, displays "Hello, world" in a message box:
RunAt="server" />
The control uses RegisterClientScriptBlock to register the client-side script block that it returns. That script block contains the __doAlert function referenced by the <input> tag's onclick attribute. It's returned only once no matter AlertButtons a page contains. RegisterClientScriptBlock should always be called from the control's OnPreRender method so ASP.NET can control the script's position in the output.
Top
between
Page.RegisterClientScriptBlock
and
RegisterClientScriptBlock is for returning blocks of client-side script containing functions. RegisterStartupScript is for returning blocks of client-script not packaged in functions-in other words, code that's to execute when the page is loaded. The latter positions script blocks near the end of the document so elements on the page that the script interacts are loaded before the script runs.
Top
Can a calendar control be customized so that it limits users to selecting certain days of the week, and only dates that fall on or after today's date?
Yes. The secret is to customize the control by processing DayRender events, which are fired as the calendar renders each and every cell. Here's an example that limits selections to future Fridays and Saturdays:
<asp:Calendar OnDayRender="OnDayRender" RunAt="server" /> . . . void OnDayRender (Object sender, DayRenderEventArgs e) { e.Day.IsSelectable = (e.Day.Date.DayOfWeek == DayOfWeek.Friday || e.Day.Date.DayOfWeek == DayOfWeek.Saturday) && e.Day.Date >= DateTime.Now; }
The DayRenderEventArgs passed to a DayRender event handler has a property named Day that identifies the day being rendered. This example sets Day's IsSelectable property to true or false depending on whether the day currently being rendered represents a legitimate selection. Setting IsSelectable to false prevents the control from placing a hyperlink in the corresponding cell, effectively making that cell unselectable.
Top
Application.Lock (); Application["ItemsSold"] = (int) Application["ItemsSold"] 1; Application["ItemsLeft"] = (int) Application["ItemsLeft"] - 1; Application.UnLock ();
By locking application state before updating it and unlocking it afterwards, you ensure that another request being processed on another thread doesn't read application state at exactly the wrong time and see an inconsistent view of it.
Top
The ASP.NET application cache doesn't have Lock and UnLock methods as application state does. Does this mean I never need to lock it?
No. It means you have to come up with your own mechanism for locking. System.Threading.ReaderWriterLock is the perfect tool for the job. Assuming rwlock is an instance of ReaderWriterLock, here's how you'd lock the application cache during an update:
rwlock.AcquireWriterLock (Timeout.Infinite); Cache["ItemsSold"] = (int) Cache ["ItemsSold"] 1; Cache["ItemsLeft"] = (int) Cache ["ItemsLeft"] - 1; rwlock.ReleaseWriterLock ();
And here's how you'd read "ItemsSold" and "ItemsLeft" values from the cache:
rwlock.AcquireReaderLock (Timeout.Infinite); int sold = (int) Cache["ItemsSold"]; int left = (int) Cache ["ItemsLeft"]; rwlock.ReleaseReaderLock ();
As with application state, locking the application cache is only necessary when performing multistep updates that are to be treated as atomic operations.
Top
If I update session state, should I lock it, too? Are concurrent accesses by multiple requests executing on multiple threads a concern with session state?
Concurrent accesses aren't an issue with session state, for two reasons. One, it's unlikely that two requests from the same user will overlap. Two, if they do overlap, ASP.NET locks down session state during request processing so that two threads can't touch it at once. Session state is locked down when the HttpApplication instance that's processing the request fires an AcquireRequestState event and unlocked when it fires a ReleaseRequestState event.
Top
ASP.NET's application cache supports expiration policies and cache removal callbacks. Expiration policies are based on time dependencies and file dependencies. Are database dependencies supported, too? In other words, can I have an item automatically removed from the cache in response to a database update?
In ASP.NET version 1.x, no. However, you can forge a link between databases and the ASP.NET application cache by combining file dependencies with database triggers. For details, and for working sample code, see Jeff Prosise's Wicked Code column in the April 2003 issue of MSDN Magazine.
Top
Do ASP.NET forms authentication cookies provide any protection against replay attacks? Do they, for example, include the client's IP address or anything else that would distinguish the real client from an attacker?
No. If an authentication cookie is stolen, it can be used by an attacker. It's up to you to prevent this from happening by using an encrypted communications channel (HTTPS). Authentication cookies issued as session cookies, do, however, include a time-out valid that limits their lifetime. So a stolen session cookie can only be used in replay attacks as long as the ticket inside the cookie is valid. The default time-out interval is 30 minutes. You can change that by modifying the timeout attribute accompanying the <forms> element in Machine.config or a local Web.config file. Persistent authentication cookies do not time-out and therefore are a more serious security threat if stolen.
Top
By default, a persistent forms authentication cookie issued by ASP.NET is valid for 50 years. Is it possible to shorten that?
Yes. Unfortunately, there is no configuration setting you can tweak to customize the lifetime of a persistent authentication cookie, but you can customize it programmatically. Here's a snippet of code that returns a persistent authentication cookie from a forms login page and limits the cookie's lifetime to 7 days:
string url = FormsAuthentication.GetRedirectUrl ("Elmo", true); FormsAuthentication.SetAuthCookie ("Elmo", true); HttpCookie cookie = Response.Cookies[FormsAuthentication.FormsCookieName]; cookie.Expires = DateTime.Now new TimeSpan (7, 0, 0, 0); Response.Redirect (url);
To set the cookie's lifetime to something other than 7 days, simply modify the TimeSpan value.
Top
I wrote an HTTP handler and registered it in the <httpHandlers> section of a local Web.config file, but the handler never gets called. What could be wrong?
In addition to being mapped to a file type (or specific file name) in a CONFIG file, an HTTP handler has to be registered in the IIS metabase. For example, if you register an HTTP handler with the Web.config file shown below, you also have to map *.igen to Aspnet_isapi.dll in the IIS metabase. Otherwise, ASP.NET doesn't see the request and can't forward it to the handler.
<configuration> <system.web> <httpHandlers> <add verb="*" path="*.igen" type="ImageGen, ImageGenLib" /> </httpHandlers> </system.web> </configuration>
Top
MailMessage and SmtpMail are classes defined in the .NET Framework Class Library's System.Web.Mail namespace. Due to a security change made to ASP.NET just before it shipped, you need to set SmtpMail's SmtpServer property to "localhost" even though "localhost" is the default. In addition, you must use the IIS configuration applet to enable localhost (127.0.0.1) to relay messages through the local SMTP service.
Top
How do I read an image from a database using ADO.NET and display it in a Web page?
The following ASPX file reads and displays an image from the Pubs database that comes with Microsoft SQL Server.
<html> <body> </body> <html> <script language="C#" runat="server"> void Page_Load(object sender, System.EventArgs e)
MemoryStream stream = new MemoryStream (); SqlConnection connection = new SqlConnection ("server=localhost;database=pubs;uid=sa;pwd="); try { connection.Open (); SqlCommand command = new SqlCommand ("select logo from pub_info where pub_id='0736'", connection); byte[] image = (byte[]) command.ExecuteScalar (); stream.Write (image, 0, image.Length); Bitmap bitmap = new Bitmap (stream); Response.ContentType = "image/gif"; bitmap.Save (Response.OutputStream, ImageFormat.Gif); } finally { connection.Close (); stream.Close (); } } </script>
Top
Some Web service classes derive from System.Web.WebServices; others do not. What's the deal?
WebService contributes properties named Application, Session, Context, Server, and User to derived classes enabling Web services to access the ASP.NET objects of the same name. If you don't use these objects in your Web service-for example, if you don't use application state or session state-then you don't have to derive from WebService, either. Incidentally, if you want to use ASP.NET session state in a Web method, use the following WebMethod attribute to enable session state for that method:
[WebMethod (EnableSession="true")]
Top
<?xml version="1.0" ?> <dynamicDiscovery xmlns="urn:schemas-dynamicdiscovery:disco.2000-03-17"> <exclude path="_vti_cnf" /> <exclude path="_vti_pvt" /> <exclude path="_vti_log" /> <exclude path="_vti_script" /> <exclude path="_vti_txt" /> </dynamicDiscovery>
How does dynamic discovery work? ASP.NET maps the file name extension VSDISCO to an HTTP handler that scans the host directory and subdirectories for ASMX and DISCO files and returns a dynamically generated DISCO document. A client who requests a VSDISCO file gets back what appears to be a static DISCO document. Note that VSDISCO files are disabled in the release version of ASP.NET. You can reenable them by uncommenting the line in the <httpHandlers> section of Machine.config that maps *.vsdisco to System.Web.Services.Discovery.DiscoveryRequestHandler and granting the ASPNET user account permission to read the IIS metabase. However, Microsoft is actively discouraging the use of VSDISCO files because they could represent a threat to Web server security.
Top
A proxy generated by Wsdl.exe has BeginAdd and EndAdd methods for calling Add asynchronously. Assuming calc is an instance of the proxy class, here's how a client calls Add asynchronously:
// Initiate an async call IAsyncResult res = calc.BeginAdd (2, 2, null, null); . . . // Get the results int sum = calc.EndAdd (res);
If the call hasn't completed when EndAdd is called, EndAdd blocks until it does. If desired, a client can ask to be notified when an asynchronous call returns by providing a reference to an AsyncCallback delegate wrapping a callback method. In the next example, EndAdd won't block because it isn't called until the client is certain the method call has returned:
IAsyncResult res = calc.BeginAdd (2, 2, cb, null); . . . public void AddCompleted (IAsyncResult res) { int sum = calc.EndAdd (res); }
Another option is to use the IsCompleted property of the IAsyncResult interface returned by BeginAdd to determine whether the call has completed and avoid calling EndAdd until it does:
IAsyncResult res = calc.BeginAdd (2, 2, null, null); . . . if (res.IsCompleted) { int sum = calc.EndAdd (res); } else { // Try again later }
Top
I wrote code that uses the SmtpMail and MailMessage classes to send e-mail from an ASP.NET application. The code worked fine in beta 2, but it throws an exception in the release version of ASP.NET. What's wrong?
Please see FAQ "How do I send e-mail from an ASP.NET (http://www.wintellect.com/resources/faqs/default.aspx?faq_id=1&page=4#4) application?"
Top
and the comment to a table named Pictures in a SQL Server database named MyPictures.
<%@ Import Namespace="System.Data.SqlClient" %> <form enctype="multipart/form-data" runat="server"> <table> <tr> <td>File name</td> <td><input type="file" id="Upload" runat="server" /></td> </tr> <tr> <td>Comment</td> <td><asp:TextBox ID="Comment" RunAt="server" /></td> </tr> <tr> <td></td> <td><asp:Button Text="Upload" OnClick="OnUpload" RunAt="server" /></td> </tr> </table> </form> <script language="C#" runat="server"> void OnUpload (Object sender, EventArgs e) { // Create a byte[] from the input file int len = Upload.PostedFile.ContentLength; byte[] pic = new byte[len]; Upload.PostedFile.InputStream.Read (pic, 0, len); // Insert the image and comment into the database SqlConnection connection = new SqlConnection ("server=localhost;database=mypictures;uid=sa;pwd="); try { connection.Open (); SqlCommand cmd = new SqlCommand ("insert into Pictures " "(Picture, Comment) values (@pic, @text)", connection); cmd.Parameters.Add ("@pic", pic); cmd.Parameters.Add ("@text", Comment.Text); cmd.ExecuteNonQuery (); } finally { connection.Close (); }
} </script>
Top
Here's an ASPX file that displays the current time of day. Once displayed, it automatically refreshes every 5 seconds:
<%@ Page Language="C#" %> <meta http-equiv="Refresh" content="5"> <html> <body> <% Response.Write (DateTime.Now.ToLongTimeString ()); %> </body> </html>
Top
How can an ASP.NET application determine whether cookies are enabled in a browser?
Determining whether cookies are enabled requires a round trip to the browser and back. If you can live with an extra round trip, the basic strategy is to return a cookie in an HTTP response and redirect to a page that checks for the cookie. Here's a page that does just that:
<html> <body> <form runat="server"> <asp:Button Text="Test Cookie Support" OnClick="OnTest" RunAt="server" /> </form> </body> </html> <script language="C#" runat="server"> void OnTest (Object sender, EventArgs e) { HttpCookie cookie = new HttpCookie ("Foo", "Bar"); Response.Cookies.Add (cookie); Response.Redirect ("OtherPage.aspx"); } </script>
And here's the page that it redirects to (OtherPage.aspx). This page uses the presence or absence of the cookie to determine whether cookies are enabled and displays the result:
<%@ Page Language="C#" %> <html> <body> <% HttpCookie cookie = Request.Cookies["Foo"]; if (cookie != null && cookie.Value == "Bar") Response.Write ("Cookies are enabled"); else Response.Write ("Cookies are not enabled"); %>
</body> </html>
Top
<%@ Page Language="C#" %> <html> <body> <% Response.Cache.SetNoStore (); Response.Write (DateTime.Now.ToLongTimeString ()); %> </body> </html>
SetNoStore works by returning a Cache-Control: private, no-store header in the HTTP response. In this example, it prevents caching of a Web page that shows the current time.
Top
How do I create a DataGrid with Delete buttons and pop up a message box asking the user for confirmation before deleting a record?
The ASPX file below demonstrates the proper technique. It populates a DataGrid with content from the Titles table of the Pubs database that comes with Microsoft SQL Server. The DataGrid's leftmost column contains a row of Delete buttons. The OnDeleteRecord method simulates a record deletion by writing the Title field of the record to be deleted to a Label control. The OnAttachScript method, which is called once for each row in the DataGrid in response to ItemCreated events, attaches to each button an OnClick attribute that activates a bit of client-side JavaScript. That script displays a confirmation dialog and prevents the page from posting back to the server (thus preventing OnDeleteRecord from being called) if the user clicks Cancel rather than OK.
<%@ Import Namespace="System.Data" %> <%@ Import Namespace="System.Data.SqlClient" %> <html> <body> <form runat="server"> <asp:DataGrid ID="MyDataGrid" AutoGenerateColumns="false" CellPadding="2" BorderWidth="1" BorderColor="lightgray" Font-Name="Verdana" Font-Size="8pt" GridLines="vertical" Width="90%" OnItemCreated="OnAttachScript" OnItemCommand="OnDeleteRecord" RunAt="server"> <Columns> <asp:ButtonColumn Text="Delete" HeaderStyle-HorizontalAlign="center"
ItemStyle-HorizontalAlign="center" ButtonType="PushButton" CommandName="Delete" /> <asp:BoundColumn HeaderText="Item ID" DataField="title_id" /> <asp:BoundColumn HeaderText="Title" DataField="title" /> <asp:BoundColumn HeaderText="Price" DataField="price" DataFormatString="{0:c}" HeaderStyle-HorizontalAlign="center" ItemStyle-HorizontalAlign="right" /> </Columns> <HeaderStyle BackColor="teal" ForeColor="white" Font-Bold="true" /> <ItemStyle BackColor="white" ForeColor="darkblue" /> <AlternatingItemStyle BackColor="beige" ForeColor="darkblue" /> </asp:DataGrid> <asp:Label ID="Output" RunAt="server" /> </form> </body> </html> <script language="C#" runat="server"> void Page_Load (Object sender, EventArgs e) { if (!IsPostBack) { SqlDataAdapter adapter = new SqlDataAdapter ("select * from titles where price != 0", "server=localhost;database=pubs;uid=sa;pwd="); DataSet ds = new DataSet (); adapter.Fill (ds); MyDataGrid.DataSource = ds; MyDataGrid.DataBind (); } } void OnAttachScript (Object sender, DataGridItemEventArgs e) { if (e.Item.ItemType == ListItemType.Item || e.Item.ItemType == ListItemType.AlternatingItem) { WebControl button = (WebControl) e.Item.Cells[0].Controls[0]; button.Attributes.Add ("onclick", "return confirm (\"Are you sure?\");"); } } void OnDeleteRecord (Object sender, DataGridCommandEventArgs e) { if (e.CommandName == "Delete") { Output.Text = "Deleted \"" e.Item.Cells[2].Text "\""; } } </script>
Top
How do I localize an ASP.NET application so that it formats dates and times based on the requestor's locale?
<%@ Import Namespace="System.Threading" %> <%@ Import Namespace="System.Globalization" %> <script language="C#" runat="server"> void Application_BeginRequest (Object sender, EventArgs e) { try { if (Request.UserLanguages.Length > 0) { Thread.CurrentThread.CurrentCulture = CultureInfo.CreateSpecificCulture (Request.UserLanguages[0]); } } catch (ArgumentException) { // Do nothing if CreateSpecificCulture fails } } </script>
Application_BeginRequest executes at the beginning of each and every request. This example reads the requestor's preferred language from the Request object's UserLanguage property (which is populated with information found in the request's Accept-Language header), creates a CultureInfo object from it, and assigns the CultureInfo object to the CurrentCulture property of the thread that's processing the request. FCL methods that are culture-aware (such as DateTime.ToShortDateString) will format dates, times, currency values, and numbers accordingly.
Top
Setting AspCompat to true does two things. First, it makes intrinsic ASP objects available to the COM components by placing unmanaged wrappers around the equivalent ASP.NET objects. Second, it improves the performance of calls that the page places to apartmentthreaded COM objects by ensuring that the page (actually, the thread that processes the request for the page) and the COM objects it creates share an apartment. AspCompat="true" forces ASP.NET request threads into single-threaded apartments (STAs). If those threads create COM objects marked ThreadingModel=Apartment, then the objects are created in the same STAs as the threads that created them. Without AspCompat="true," request threads run in a multithreaded apartment (MTA) and each call to an STA-based COM object incurs a performance hit when it's marshaled across apartment boundaries. Do not set AspCompat to true if your page uses no COM objects or if it uses COM objects that don't access ASP intrinsic objects and that are registered ThreadingModel=Free or
ThreadingModel=Both.
Top
I've developed a custom Windows Forms control that I'd like to use in a Web Form. I've heard that ASP.NET can use Windows Forms controls. Is that true? If so, how do I create a Windows Forms control in a Web Form?
You can embed Windows Forms controls in Web pages using <object> tags similar to those that declare ActiveX controls. To demonstrate, here's the source code for a very simple slider control-one that derives from the FCL's TrackBar class:
Compile this source code into a DLL with the following command (assuming the source code file is named Controls.cs):
Copy the resulting DLL (Controls.dll) to the virtual directory of your choice on your Web server. Now create a text file named Slider.aspx in the same directory and add the following HTML:
<html> <body> <form> <object id="Slider" classid="http:Controls.dll#Wintellect.WebSlider" width="64" height="256"> <param name="Minimum" value="0"> <param name="Maximum" value="10"> <param name="Value" value="3"> <param name="Orientation" value="Vertical"> <param name="BackColor" value="gainsboro"> </object> </form> </body> </html>
The <object> tag declares an instance of the control and names it Slider. It also uses <param> tags to initialize some of the control's properties. Open the ASPX file in your browser and the slider control should be displayed. IE downloads the control implementation from the Web server, so you don't have to install Controls.dll on the client. The client must, however, have the .NET Framework installed. Internet Explorer 5.01 or higher is required on the client, too.
Top
If I use the slider control in the previous example in a Web page, what must I do to allow a server-side event handler to determine the position of the slider's thumb?
The trick is to intercept the form submit event fired before the form posts back to the server and add the thumb position to the form's postback data. Here's a modified version of the ASPX file in the previous example that does just that. The onsubmit attribute in the <form> tag calls the JavaScript function SubmitForm before the form posts back to the server. SubmitForm writes the slider's thumb position to a hidden <input> control named __THUMBPOS. The browser submits the __THUMBPOS control's value to the server, and the server-side event handler extracts the value from the request. In this example, the event handler writes the thumb position to the Web page.
<html> <body> <form id="MyForm" onsubmit="javascript:return SubmitForm ();" runat="server"> <input type="hidden" name="__THUMBPOS" value="" > <object id="Slider" classid="http:Controls.dll#Wintellect.WebSlider" width="64" height="256"> <param name="Minimum" value="0"> <param name="Maximum" value="10"> <param name="Value" value="3"> <param name="Orientation" value="Vertical"> <param name="BackColor" value="gainsboro"> </object> <br><br> <input type="submit" value="Test" onserverclick="ShowThumbPos" runat="server"> <br><br> <span id="Output" runat="server" /> </form> </body> </html> <script language="JavaScript"> function SubmitForm () { MyForm.__THUMBPOS.value = MyForm.Slider.value; return true; } </script> <script language="C#" runat="server"> void ShowThumbPos (Object sender, EventArgs e) { Output.InnerText = "You chose " Request["__THUMBPOS"]; } </script>
Top
If I use the slider control in the previous example in a Web page, the slider's thumb snaps back to its default position each time the page posts back to the server. Is there a way to make the thumb stay put?
The ASPX file below shows one way to do it. The <param> tag that controls the slider's thumb position is no longer embedded in the page's HTML; instead, it's output
programmatically with Response.Write. That enables the page to emit a <param> tag containing a default value if the page is fetched outside of a postback, or a <param> tag containing the __THUMBPOS value submitted in the request if the page is being returned following a postback. It's not pretty, but it works.
<%@ Page Language="C#" %> <html> <body> <form id="MyForm" onsubmit="javascript:return SubmitForm ();" runat="server"> <input type="hidden" name="__THUMBPOS" value=""> <object id="Slider" classid="http:Controls.dll#Wintellect.WebSlider" width="64" height="256"> <param name="Minimum" value="0"> <param name="Maximum" value="10"> <% if (Request["__THUMBPOS"] == null) Response.Write ("<param name=\"Value\" value=\"3\">\r\n"); else Response.Write ("<param name=\"Value\" value=\"" Request["__THUMBPOS"] "\">\r\n"); %> <param name="Orientation" value="Vertical"> <param name="BackColor" value="gainsboro"> </object> <br><br> <input type="submit" value="Test" onserverclick="ShowThumbPos" runat="server"> <br><br> <span id="Output" runat="server" /> </form> </body> </html> <script language="JavaScript"> function SubmitForm () { MyForm.__THUMBPOS.value = MyForm.Slider.value; return true; } </script> <script language="C#" runat="server"> void ShowThumbPos (Object sender, EventArgs e) { Output.InnerText = "You chose " Request["__THUMBPOS"]; } </script>
Top
How can I create a DataGrid that displays a column of images obtained from a database?
The following ASPX file demonstrates how:
<%@ Import Namespace="System.Data" %> <%@ Import Namespace="System.Data.SqlClient" %> <html> <body> <form runat="server"> <asp:DataGrid ID="MyDataGrid" RunAt="server" AutoGenerateColumns="false" CellPadding="2" BorderWidth="1" BorderColor="lightgray" Font-Name="Verdana" Font-Size="8pt" GridLines="vertical" Width="100%"> <Columns> <asp:TemplateColumn HeaderText="Photo" HeaderStyle-HorizontalAlign="center"> <ItemTemplate> <center> <img src='<%# "NorthwindImageGrabber.ashx?id=" DataBinder.Eval (Container.DataItem, "EmployeeID") %>'> </center> </ItemTemplate> </asp:TemplateColumn> <asp:BoundColumn HeaderText="Last Name" HeaderStyle-HorizontalAlign="center" DataField="LastName" /> <asp:BoundColumn HeaderText="First Name" HeaderStyle-HorizontalAlign="center" DataField="FirstName" /> <asp:BoundColumn HeaderText="Title" HeaderStyle-HorizontalAlign="center" DataField="Title" /> </Columns> <HeaderStyle BackColor="teal" ForeColor="white" Font-Bold="true" /> <ItemStyle BackColor="white" ForeColor="darkblue" /> <AlternatingItemStyle BackColor="beige" ForeColor="darkblue" /> </asp:DataGrid> </form> </body> </html> <script language="C#" runat="server"> void Page_Load (Object sender, EventArgs e) { if (!IsPostBack) { SqlConnection connection = new SqlConnection ("server=localhost;database=northwind;uid=sa;pwd="); try { connection.Open (); SqlCommand command = new SqlCommand ("select employeeid, lastname, firstname, title from employees", connection); SqlDataReader reader = command.ExecuteReader (); MyDataGrid.DataSource = reader; MyDataGrid.DataBind (); } finally { connection.Close (); } } } </script>
This ASPX file contains a DataGrid that displays data from the Employees table of SQL Server's Northwind database. Each row rendered by the DataGrid represents one employee, and each row's leftmost column contains the employee's picture. The picture comes from the table's Photo field. The image is rendered by the <ItemTemplate> tag, which emits an <img> tag accompanied by a src attribute that points to a file named NorthwindImageGrabber.ashx. Inside the ASHX file is an HTTP handler that retrieves an image from the database. A query string appended to the URL tells the handler which image to retrieve. Here's NorthwindImageGrabber.ashx's source code:
<%@ WebHandler Language="C#" Class="ImageGrabber" %> using using using using using using System; System.Web; System.Drawing; System.Drawing.Imaging; System.Data.SqlClient; System.IO;
public class ImageGrabber : IHttpHandler { public void ProcessRequest (HttpContext context) { string id = (string) context.Request["id"]; if (id != null) { MemoryStream stream = new MemoryStream (); SqlConnection connection = new SqlConnection ("server=localhost;database=northwind;uid=sa;pwd="); Bitmap bitmap = null; Image image = null; try { connection.Open (); SqlCommand cmd = new SqlCommand ("select photo from employees where employeeid='" id "'", connection); byte[] blob = (byte[]) cmd.ExecuteScalar (); stream.Write (blob, 78, blob.Length - 78); bitmap = new Bitmap (stream); // Shrink the image, but maintain its aspect ratio int width = 48; int height = (int) (width * ((double) bitmap.Height / (double) bitmap.Width)); image = bitmap.GetThumbnailImage (width, height, null, IntPtr.Zero); context.Response.ContentType = "image/jpeg"; image.Save (context.Response.OutputStream, ImageFormat.Jpeg); } finally { if (image != null) image.Dispose (); if (bitmap != null) bitmap.Dispose (); stream.Close (); connection.Close (); } } }
The ProcessRequest method, which is called every time the ASHX file is requested, retrieves the image from the database and returns it to the client as a JPEG. For good measure, it also shrinks the image down to thumbnail size using Image.GetThumbnailImage. NorthwindImageGrabber.ashx discards the first 78 bytes of each image because the Northwind database's Photo field doesn't store raw images; it stores BMP bitmaps prefixed by 78 bytes of unrelated header information.
Top
Is it possible to write an ASP.NET handler that works like an ISAPI filter-that is, that sees requests and responses and perhaps modifies them, too?
You can do it by writing an HTTP module-a class that implements IHttpModule-and registering it in Web.config. Here's a simple HTTP module written in C# that appends "Hello, world" to every response:
using System; using System.Web; public class MyModule : IHttpModule { public void Init (HttpApplication application) { application.EndRequest = new EventHandler (Application_EndRequest); } void Application_EndRequest (Object source, EventArgs e) { HttpApplication application = (HttpApplication) source; HttpContext context = application.Context; context.Response.Write ("Hello, world"); } public void Dispose () { }
<configuration> <system.web> <httpModules> <add name="MyModule" type="MyModule, CustomModules" /> </httpModules> </system.web> </configuration>
An HTTP module can handle the per-request events fired by HttpApplication instances, and it can fire events of its own that can be processed in Global.asax. To deploy the module, simply drop the DLL containing MyModule into the application root's bin subdirectory.
Top
How can ASP.NET apps transmit data from one page to another?
One way to transfer data from page to page is to have the sending page encode the data in a query string and the receiving page read the data from the request. Here's the source code for a page named PageOne.aspx that encodes a string typed by the user in a query string and passes it to PageTwo.aspx:
<!-- PageOne.aspx --> <html> <body> <form runat="server"> <asp:TextBox ID="Input" RunAt="server" /> <asp:Button Text="NextPage" OnClick="OnNextPage" RunAt="server" /> </form> </body> </html> <script language="C#" runat="server"> void OnNextPage (Object sender, EventArgs e) { Response.Redirect ("PageTwo.aspx?Input=" Input.Text); } </script>
And here's the page it redirects to, which echoes what the user typed:
<!-- PageTwo.aspx --> <%@ Page Language="C#" %> <html> <body> <% Response.Write ("You typed \"" Request["Input"] "\""); %> </body> </html>
Another way to pass data from one page to another--a technique that has the added benefit of keeping the data on the server and not exposing it to the user--is to pass the data in session state, as demonstrated here:
<!-- PageOne.aspx --> <html> <body> <form runat="server"> <asp:TextBox ID="Input" RunAt="server" /> <asp:Button Text="NextPage" OnClick="OnNextPage"
RunAt="server" /> </form> </body> </html> <script language="C#" runat="server"> void OnNextPage (Object sender, EventArgs e) { Session["Input"] = Input.Text; Response.Redirect ("PageTwo.aspx"); } </script> <!-- PageTwo.aspx --> <%@ Page Language="C#" %> <html> <body> <% Response.Write ("You typed \"" Session["Input"] "\""); %> </body> </html>
If you use Server.Transfer rather than Response.Redirect to transfer control to another page, you can use public fields in the sending page's code-behind class to transmit data. The following example demonstrates how:
<!-- PageOne.aspx --> <%@ Page Inherits="PageOneClass" %> <html> <body> <form runat="server"> <asp:TextBox ID="Input" RunAt="server" /> <asp:Button Text="NextPage" OnClick="OnNextPage" RunAt="server" /> </form> </body> </html> // PageOneClass.cs using System; using System.Web.UI; using System.Web.UI.WebControls; public class PageOneClass : Page { public string _Input; protected TextBox Input; public void OnNextPage (Object sender, EventArgs e) { _Input = Input.Text; Server.Transfer ("PageTwo.aspx"); } } <!-- PageTwo.aspx --> <%@ Page Language="C#" %> <html> <body> <%
PageOneClass prevpage = (PageOneClass) Context.Handler; Response.Write ("You typed \"" prevpage._Input "\""); %> </body> </html>
Top
How do I initialize a TextBox whose TextMode is "password" with a password? Initializing the TextBox's Text property doesn't seem to work.
This won't work:
<asp:TextBox TextMode="Password" ID="Password" RunAt="server" /> <script language="C#" runat="server"> void Page_Load (Object sender, EventArgs e) { Password.Attributes.Add ("value", "imbatman"); } </script>
The latter code fragment manually adds a value="imbatman" attribute to the <input> tag output by the TextBox control, causing the specified text to appear in the TextBox. You can also initialize a password TextBox by including a Value attribute in the control tag, as demonstrated below:
I know I can write custom server controls by deriving from Control or WebControl. But can I modify the behavior of existing controls by deriving from them and modifying their output?
You bet. Here's a custom control named NumTextBox that derives from TextBox and adds an onkeydown attribute to the <input> tag that TextBox outputs. That attribute references a local JavaScript function that filters out non-numeric keys, producing a TextBox that accepts only numbers. For good measure, NumTextBox senses the browser type and renders differently to Internet Explorer and Netscape Navigator, enabling it to work in either browser. It also overrides TextBox's Text property and implements a set accessor that throws an exception if a non-numeric string is written to it.
using System; using System.Web.UI; using System.Web.UI.WebControls; namespace Wintellect { public class NumTextBox : TextBox { string IEClientScriptBlock = "<script language=\"javascript\">\n" "<!--\n" "var keys = new Array (8, 9, 13, 33, 34, 35, " "36, 37, 39, 45, 46);\n" "function isKeyValid (keyCode)\n" "{\n" " return ((keyCode >= 48 && keyCode <= 57) || " "isAuxKey (keyCode));\n" "}\n" "function isAuxKey (keyCode)\n" "{\n" " for (i=0; i<keys.length; i )\n" " if (keyCode == keys[i])\n" " return true;\n" " return false;\n" "}\n" "-->\n" "</script>"; string NetscapeClientScriptBlock = "<script language=\"javascript\">\n" "<!--\n" "function isKeyValid (keyCode)\n" "{\n" " return ((keyCode >= 48 && keyCode <= 57) || " "keyCode == 8 || keyCode == 13);\n" "}\n" "-->\n" "</script>"; public override string Text { get { return base.Text; } set { // Make sure value is numeric before storing it Convert.ToInt64 (value); base.Text = value; } } protected override void OnPreRender (EventArgs e) { string browser = Context.Request.Browser.Type.ToUpper ();
int version = Context.Request.Browser.MajorVersion; if (browser.IndexOf ("IE") > -1 && version >= 4) Page.RegisterClientScriptBlock ("NumTextBoxScript", IEClientScriptBlock); else if (browser.IndexOf ("NETSCAPE") > -1 && version >= 4) Page.RegisterClientScriptBlock ("NumTextBoxScript", NetscapeClientScriptBlock); } protected override void Render (HtmlTextWriter writer) { string browser = Context.Request.Browser.Type.ToUpper (); int version = Context.Request.Browser.MajorVersion; if (browser.IndexOf ("IE") > -1 && version >= 4) writer.AddAttribute ("onkeydown", "javascript:return isKeyValid (window.event.keyCode)"); else if (browser.IndexOf ("NETSCAPE") > -1 && version >= 4) writer.AddAttribute ("onkeydown", "javascript:return isKeyValid (event.which)"); } } } base.Render (writer);
Here's an ASPX file you can use to test the control. It assumes that the control is compiled into an assembly named NumTextBoxControl.
<%@ Register TagPrefix="win" Namespace="Wintellect" Assembly="NumTextBoxControl" %> <html> <body> <form runat="server"> <win:NumTextBox runat="server" /> </form> </body> </html>
Top
Is it possible to associate hidden values--say, values from an identity field in a database table--with items in a DataGrid?
You bet. Just declare a BoundColumn in the DataGrid and set its Visible property to false, like so:
The column won't show up in the DataGrid, but you'll be able to read data from it following a postback just as if it were visible.
Top
<%@ Import Namespace="System.Data" %> <%@ Import Namespace="System.Data.SqlClient" %> <html> <body> <form runat="server"> <asp:DataGrid ID="MyDataGrid" AutoGenerateColumns="false" RunAt="server"> <Columns> <asp:TemplateColumn HeaderText="Number" ItemStyleHorizontalAlign="center"> <ItemTemplate> <%# rownum %> </ItemTemplate> </asp:TemplateColumn> <asp:BoundColumn HeaderText="Title" DataField="title" /> </Columns> <HeaderStyle HorizontalAlign="center" /> </asp:DataGrid> </form> </body> </html> <script language="C#" runat="server"> int rownum = 1; void Page_Load (Object sender, EventArgs e) { if (!IsPostBack) { SqlDataAdapter adapter = new SqlDataAdapter ( "select title from titles where price != 0", "server=localhost;database=pubs;uid=sa" ); DataSet ds = new DataSet (); adapter.Fill (ds); MyDataGrid.DataSource = ds; MyDataGrid.DataBind (); } } </script>
Top
Is it possible to call Fill on a DataAdapter and fill two DataTables in a DataSet with a single call?
You bet. Here's a sample that demonstrates how by performing a double query and binding each of the resulting DataTables to a different DataGrid.
<%@ Import Namespace="System.Data" %> <%@ Import Namespace="System.Data.SqlClient" %> <html> <body> <form runat="server"> <asp:DataGrid ID="DataGrid1" RunAt="server" /> <asp:DataGrid ID="DataGrid2" RunAt="server" /> </form> </body> </html> <script language="C#" runat="server"> void Page_Load (Object sender, EventArgs e) { if (!IsPostBack) { SqlDataAdapter adapter = new SqlDataAdapter ( "select * from titles; select * from authors", "server=localhost;database=pubs;uid=sa" ); DataSet ds = new DataSet (); adapter.Fill (ds); DataGrid1.DataSource = ds.Tables[0]; DataGrid1.DataBind (); DataGrid2.DataSource = ds.Tables[1]; DataGrid2.DataBind (); } } </script>
Top
I'm trying to use Server.CreateObject to instantiate a legacy COM component in an ASPX page. If I use VB.NET, I can create and call the COM object just fine. But the same code written in C# doesn't compile. The compiler complains that the method I'm calling isn't a member of Object. What gives?
You've discovered an interesting feature of VB.NET--namely, that it trades type safety for simplicity when late binding to COM objects. Check out the following VB.NET code sample, which instantiates a COM object (ProgID="Wintellect.Math") and calls its Add method to add 2 and 2:
Dim Sum As Integer Dim WinMath As Object WinMath = Server.CreateObject ("Wintellect.Math") Sum = WinMath.Add (2, 2)
This code works just fine, despite that fact that Add is not a member of System.Object. The VB.NET compiler relaxes its type-checking rules to simplify your code. The C# compiler, however, does not. The following code won't compile:
The solution for C# programmers is to late-bind to the COM object System.Type.InvokeMember. Here's the C# equivalent of the VB.NET code above:
using
Type t = Type.GetTypeFromProgID ("Wintellect.Math"); Object math = Server.CreateObject (t); Object[] args = { 2, 2 }; int sum = (int) t.InvokeMember ("Add", BindingFlags.InvokeMethod, null, math, args);
It's not pretty, but it works, and it perfectly illustrates the extra effort required to accomplish late binding in C#.
Top
I'm trying to use ASP.NET's HtmlInputFile control to upload files to a Web server, but the control's PostedFile property is always nulleven after I select a file and post back to the server. What am I doing wrong?
Most likely you forgot to include an enctype="multipart/form-data" attribute in your <form> tag. The following HTML form doesn't support file uploads:
But the next one does. Decorate the <form> tag as shown here and file uploads will work just fine:
ASP.NET's @ OutputCache directive lets me cache different versions of a page based on varying input parameters, HTTP headers, and browser types. I'd also like to be able to cache based on varying session IDs. Is that possible?
You bet. Here's a sample page that uses a VaryByCustom attribute to cache different versions of a page based on session IDs:
<%@ Page Language="C#" %> <%@ OutputCache Duration="10" VaryByParam="None" VaryByCustom="SessionID" %> <html> <body> <%
Session["MyData"] = "Foo"; // Make the session persistent Response.Write ("Your session ID is " Session.SessionID); Response.Write ("<br>"); Response.Write ("The current time is " DateTime.Now.ToLongTimeString ()); %> </body> </html>
In order for VaryByCustom="SessionID" to work, you must include in the application root a Global.asax file containing the following GetVaryByCustomString method:
<script language="C#" runat="server"> public override string GetVaryByCustomString (HttpContext context, string arg) { if (arg.ToLower () == "sessionid") { HttpCookie cookie = context.Request.Cookies["ASP.NET_SessionId"]; if (cookie != null) return cookie.Value; } return base.GetVaryByCustomString (context, arg); } </script>
GetVaryByCustomString is a mechanism for extending ASP.NET's page output cache. This implementation of GetVaryByCustomString, which overrides the one inherited from HttpApplication, responds to a VaryByCustom="SessionID" attribute in an @ OutputCache directive by returning the current session ID, if present. ASP.NET responds by caching different versions of the page if the session IDs differ. Note that GetVaryByCustomString extracts the session ID from the session cookie, not from the session's SessionID property. That's because the request has yet to be associated with a session when GetVaryByCustomString is called. An unpleasant side effect is that this technique doesn't work with cookieless session state. Also note that the page won't be cached until a user requests the page for the second time, because the first request lacks a valid session cookie.
Top
I've noticed that DataGrids round-trip tons of information in view state, decreasing the effective bandwidth of the connection. Is there anything I can do to reduce the DataGrid's view state usage?
You bet. Set the DataGrid's EnableViewState property to false, as shown here:
Once you make this change, you'll also have to reinitialize the DataGrid in every request (even during postbacks), because the DataGrid will no longer retain its state across postbacks. In other words, instead of doing this:
Do this:
The performance you lose may be more than compensated for by the effective bandwidth you gain--especially if instead of querying a database on every request, you query once, cache the data, and initialize the DataGrid from the cache.
Top
Is it possible to create a DataGrid that uses scrolling rather than paging to provide access to a long list of items?
With a little help from a <div> tag, yes. The following ASPX file displays the "Products" table of SQL Server's Northwind database in a scrolling table:
<%@ Import Namespace="System.Data.SqlClient" %> <html> <body> <form runat="server"> <div style="height: 256px; overflow: auto"> <asp:DataGrid ID="MyDataGrid" Width="100%" RunAt="server" /> </div> </form> </body> </html> <script language="C#" runat="server"> void Page_Load (Object sender, EventArgs e) { if (!IsPostBack) { SqlConnection connection = new SqlConnection ("server=localhost;database=northwind;uid=sa"); try { connection.Open (); SqlCommand command = new SqlCommand ("select * from products", connection); SqlDataReader reader = command.ExecuteReader (); MyDataGrid.DataSource = reader; MyDataGrid.DataBind (); } finally { connection.Close (); } } }
</script>
Top
By default, ASP.NET's worker process uses the identity of a special account named ASPNET that's created when ASP.NET is installed. By including a user name and password in Machine.config's element, I can configure ASP.NET to run using an account of my choosing. However, my company has a strict policy against encoding plaintext passwords in configuration files. I want to specify the account that ASP.NET uses, but I also need to secure the password. Is that possible?
Out of the box, ASP.NET 1.0 requires the user name and password to be encoded in plaintext in Machine.config. However, you can obtain a patch from Microsoft that allows the user name and password to be stored in encrypted form in a secure registry key that's off limits to nonadministrators. The hotfix also allows you to secure the credentials used to access ASP.NET's ASPState database when using SQL Server to store session state. You'll find instructions for obtaining the hotfix (and information about a helper utility that encrypts user names and passwords for you) at http://support.microsoft.com/default.aspx? scid=kb;en-us;Q329250. Microsoft plans to build the hotfix into the Everett" release of the .NET Framework, which will ship with Windows .NET Server. Be aware, however, that Windows .NET Server comes with IIS 6.0, which supports an alternative means for securing process credentials. Applications can be assigned to arbitrary application pools, and application pools can be assigned unique identities using encrypted credentials stored in the IIS metabase. As Microsoft security guru Erik Olsen recently noted, this is probably the better long-term direction for companies whose policies prevent them from storing plaintext credentials in CONFIG files.
Top
I want to make DataGrid paging more efficient by using custom paging. Oracle makes custom paging easy by supporting query-by-row-number. SQL Server is just the opposite. There's no obvious way to ask SQL Server for, say, rows 101-150 in a 500-row result set. What's the best way to do custom paging when you have SQL Server on the back end?
You can use a query of the following form to retrieve records by row number from Microsoft SQL Server:
SELECT * FROM (SELECT TOP {0} * FROM (SELECT TOP {1} * FROM {2} ORDER BY {3}) AS t1 ORDER BY {3} DESC) AS t2 ORDER BY {3}
Replace {0} with the page size (the number of records displayed on each page), {1} with the page size * page number (1-based), {2} with the name of the table you wish to query, and {3} with a field name. The following example retrieves rows 41-50 from the "Products" table of the Northwind database:
SELECT * FROM (SELECT TOP 10 * FROM (SELECT TOP 50 * FROM Products ORDER BY ProductID) AS t1 ORDER BY ProductID DESC) AS t2 ORDER BY ProductID
You can combine this query technique with custom paging to make DataGrid paging more efficient. With default paging, you must initialize the data source with all records displayed on all pages. With custom paging, you can initialize the data source with just those records that pertain to the current page.
Top
Is it possible to prevent a Web form from scrolling to the top of the page when it posts back to the server?
One way to do it is to add a SmartNavigation="true" attribute to the page's @ Page directive. That requires Internet Explorer 5.0 or higher on the client. To prevent unwanted scrolling in a wider range of browsers, you can use a server-side script that generates client-side script. The first step is to replace the page's <body> tag with the following statements:
<% if (Request["__SCROLLPOS"] != null && Request["__SCROLLPOS"] != String.Empty) { int pos = Convert.ToInt32 (Request["__SCROLLPOS"]); Response.Write ("<body id=\"theBody\" " "onscroll=\"javascript:document.forms[0].__SCROLLPOS.value = "theBody.scrollTop;\" " "onload=\"javascript:theBody.scrollTop=" pos ";\">"); } else { Response.Write ("<body id=\"theBody\" " "onscroll=\"javascript:document.forms[0].__SCROLLPOS.value =" "theBody.scrollTop;\">"); %> }
"
Step two is to add the following line somewhere between the <form runat="server"> and </form> tags:
How does it work? The server-side script block outputs a <body> tag containing an onscroll attribute that keeps tabs on the scroll position and an onload attribute that restores the last scroll position following a postback. The scroll position is transmitted to the server in a hidden <input> control named __SCROLLPOS. Note that this technique is compatible with Internet
Top
If I use the same user control in two different pages and include an @ OutputCache directive in the ASCX file, will the user control be cached once or twice?
In ASP.NET version 1.0, the control will be cached twice. In version 1.1, you can include a Shared="true" attribute in the @ OutputCache directive to cache the control just once.
Top
Top
How do I set the focus to a specific control when a Web form loads?
With a dash of client-side script. Upon loading, the following page sets the focus to the first of its three TextBox controls:
<html> <body> <form runat="server"> <asp:TextBox ID="TextBox1" RunAt="server" /><br> <asp:TextBox ID="TextBox2" RunAt="server" /><br> <asp:TextBox ID="TextBox3" RunAt="server" /> </form> </body> </html> <script language="javascript"> document.forms[0].TextBox1.focus (); </script>
Top
Is it possible to post a page containing a runat="server" form to a page other than itself? Adding an action attribute to the <form> tag is futile because ASP.NET overrides it with an action attribute that points back to the same page.
ASP.NET forbids a runat=server" form from posting back to another page, but you may be able to accomplish what you're after by taking a slightly different approach. It turns out that if
you remove runat=server" from the <form> tag, ASP.NET won't alter the tag's action attribute. The bad news is that a form lacking a runat=server" attribute can't host Web controls. The good news is that it can host HTML controls, which means that if you can do without DataGrids and other rich controls, you can post back to other pages just fine. The following page contains a login form that hosts three HTML controls. Note the runat=server" attributes adorning the controls but the absence of runat="server" in the <form> tag:
<html> <body> <form action="Welcome.aspx"> <table cellpadding="8"> <tr> <td>User Name:</td> <td><input type="text" ID="UserName" runat="server" /></td> </tr> <tr> <td>Password:</td> <td><input type="password" ID="Password" runat="server" /></td> </tr> <tr> <td></td> <td><input type="submit" value="Log In" runat="server" /></td> </tr> </table> </form> </body> </html>
Clicking the Log In button submits the form to Welcome.aspx, which reads the user name that the user typed from the HTTP request and outputs it to the page. Here's Welcome.aspx:
<html> <body> <h1><asp:Label ID="Output" RunAt="server" /></h1> </body> </html> <script language="C#" runat="server"> void Page_Load (Object sender, EventArgs e) { Output.Text = "Hello, " Request["UserName"]; } </script>
Do the runat=server" attributes on the <input> tags do anything useful? You bet. They make the controls visible to server-side scripts.
Top
When configured to store session state in a SQL Server database, ASP.NET stores sessions in the Tempdb database, which means sessions are lost if the database server is rebooted. Is it possible to configure ASP.NET to store session in the
Top
I'm using an <error> element in Web.config to redirect to a custom error page when a 404 error occurs. Inside the error page, I want to retrieve the URL that caused the 404 error so I can write it to a log file. How do I get that URL?
When ASP.NET redirects to a custom error page, it passes the original URL (the one that caused the error) in a query string. Here's an example in which PageNotFound.aspx is the custom error page and foo.aspx is the page that produced the error: http://localhost/PageNotFound.aspx?aspxerrorpath=/foo.aspx PageNotFound.aspx can therefore obtain the URL of the page that generated the error (in your case, the nonexistent page that resulted in a 404 error) like this:
If you'd like to retrieve the IP address from which the request originated for logging purposes as well, read it from Request.ServerVariables["REMOTE_ADDR"] or Request.UserHostAddress.
Top
How does System.Web.UI.Page's IsPostBack property work? How does it determine whether a request is a postback?
IsPostBack checks to see whether the HTTP request is accompanied by postback data containing a __VIEWSTATE or __EVENTTARGET parameter. No parameters, no postback.
Top
Forms authentication protects ASPX files and other resources owned by ASP.NET, but it does not restrict access to HTML files and other non-ASP.NET resources. Is it possible to extend forms authentication to protect ordinary HTML files and other resources that don't belong to ASP.NET?
The best way to do it is map *.htm, *.html, and other file name extensions to Aspnet_isapi.dll in the IIS metabase. (You can use the IIS configuration manager to do the mapping.) Transferring ownership of these resources to ASP.NET degrades performance a bit, but it
Top
<%@ Import Namespace="System.Data.SqlClient" %> <html> <body> <form runat="server"> <asp:DataGrid ID="MyDataGrid" RunAt="server" AutoGenerateColumns="false" CellPadding="2" BorderWidth="1" BorderColor="lightgray" Font-Name="Verdana" Font-Size="8pt" GridLines="vertical" Width="100%"> <Columns> <asp:TemplateColumn HeaderText="Buy" ItemStyle-HorizontalAlign="center"> <ItemTemplate> <asp:CheckBox RunAt="server" /> </ItemTemplate> </asp:TemplateColumn> <asp:BoundColumn HeaderText="Title" DataField="title" /> <asp:BoundColumn HeaderText="Price" DataField="price" DataFormatString="{0:c}" ItemStyle-HorizontalAlign="right" /> </Columns> <HeaderStyle BackColor="teal" ForeColor="white" Font-Bold="true" HorizontalAlign="center" /> <ItemStyle BackColor="white" ForeColor="darkblue" /> <AlternatingItemStyle BackColor="beige" ForeColor="darkblue" /> </asp:DataGrid> <br> <asp:Button Text="Check Out" OnClick="OnCheckOut" RunAt="server" /> <br><br> <asp:Label ID="Output" RunAt="server" /> </form> </body> </html> <script language="C#" runat="server"> void Page_Load (Object sender, EventArgs e) { if (!IsPostBack) { SqlConnection connection = new SqlConnection ("server=localhost;database=pubs;uid=sa;pwd="); try { connection.Open ();
SqlCommand command = new SqlCommand ("select * from titles where price != 0", connection); SqlDataReader reader = command.ExecuteReader (); MyDataGrid.DataSource = reader; MyDataGrid.DataBind ();
void OnCheckOut (Object sender, EventArgs e) { StringBuilder builder = new StringBuilder (1024); foreach (DataGridItem item in MyDataGrid.Items) { if (item.ItemType == ListItemType.Item || item.ItemType == ListItemType.AlternatingItem) { CheckBox box = (CheckBox) item.Cells[0].Controls[1]; if (box.Checked) { builder.Append (item.Cells[1].Text); builder.Append ("<br>"); } } } Output.Text = builder.ToString (); } </script>
Top
I'm dynamically adding columns to a DataGrid at run-time, but am finding that the DataGrid's events don't fire properly. Interestingly, if I define the columns statically, the events work just fine. Any idea what I'm doing wrong?
You're probably adding the columns to the DataGrid in Page_Load. Add them in Page_Init instead and the events will fire just fine. In general, Page_Init is the ideal place to modify the page and its controls, while Page_Load is the place to modify control state. Keep this simple dictum in mind and you'll save yourself a lot of headaches down the road.
Top
When hosting a Windows Forms control in a Web page, is it possible to connect the control's events to a handler implemented in client-side script?
You bet. But you, the control developer, have to do a little work to allow it to happen. Here's a summary of the steps required: 1) Declare in the control DLL an interface containing methods whose names and signatures match the events you wish to expose to client-side script. Attribute the interface [InterfaceType (ComInterfaceType.InterfaceIsDispatch)] so the Framework will expose it as an IDispatch interface to COM clients. Use [DispId] attributes to assign each of the interface's methods a unique dispatch ID.
2) Adorn the control class with a [ComSourceInterfaces] attribute naming the interface defined in step 1. This instructs the Framework to expose the interface's methods to COM clients as COM events. 3) Grant the assembly containing the control (the control DLL) full trust on the client. You can use the Microsoft .NET Framework Wizards found in Control Panel/Administrative Tools to grant the assembly full trust. To demonstrate, here's a WebSlider control whose Scroll events (which are inherited from and fired by the TrackBar base class) can be processed in a browser:
using System; using System.Runtime.InteropServices; namespace Wintellect { [ComSourceInterfaces (typeof (IWebSliderEvents))] public class WebSlider : System.Windows.Forms.TrackBar {} [InterfaceType (ComInterfaceType.InterfaceIsIDispatch)] public interface IWebSliderEvents { [DispId (1)] void Scroll (Object sender, EventArgs e); } }
And here's a Web page that you can use to test the control and prove that you can respond to its events in client-side script:
<html> <body> <form id="MyForm"> <object id="Slider" classid="http:WebSlider2.dll#Wintellect.WebSlider" width="64" height="256"> <param name="Minimum" value="0"> <param name="Maximum" value="10"> <param name="Value" value="0"> <param name="Orientation" value="Vertical"> <param name="BackColor" value="gainsboro"> </object> </form> <span id="Output" runat="server">0</span> </body> </html> <script for="Slider" event="Scroll (source, args)" language="javascript"> Output.innerText = MyForm.Slider.value; </script>
As you move the slider's thumb, the client-side event handler continually updates the positional value beneath the slider to reflect the thumb's latest position.
Top
How does ASP.NET generate session IDs? Are they random? Predictable session IDs increase the risk of session hijacking.
Relax: ASP.NET uses random, non-sequential session IDs. Specifically, it uses the FCL's System.Security.Cryptography.RNGCryptoServiceProvider class to generate highly random 120-bit session IDs. Sessions can still be hijacked by stealing session cookies or, if cookieless session state is being used, by reading session IDs from the browser's address bar. But ASP.NET's use of random session IDs should preclude the possibility of hijacking sessions by guessing session IDs.
Top
Running ASP.NET on a Web farm requires you to configure each server to use identical validation and encryption keys. Is there a tool available for producing those keys?
You can get the tool you're looking for right here. KeyGen is a command-line utility for producing validation and encryption keys of any length. Click here to download it, and here to download the C# source code. To run it, simply type KeyGen followed by a key length in bytes. The following command produces a 24-byte key:
keygen 24
KeyGen uses the .NET Framework Class Library's System.Security.Cryptography.RNGCryptoServiceProvider class to generate cryptographically strong keys. As such, it only runs on machines equipped with the .NET Framework.
Top
How can I get the name of the Windows security principal that a request is executing as? Page.User.Identity.Name is no help at all when forms authentication is used.
The best way to do it is to use P/Invoke to call the Win32 GetUserName function. The ASPX file below demonstrates how. In order for this to work, your code must be running with a high-enough trust level to permit callouts to unmanaged code.
<%@ Import Namespace="System.Runtime.InteropServices" %> <html> <body> <asp:Label ID="Output" RunAt="server" /> </body> </html> <script language="C#" runat="server"> [DllImport ("Advapi32.dll")] static extern bool GetUserName (StringBuilder name, ref int size); void Page_Load (Object sender, EventArgs e)
} </script>
Top
StringBuilder name = new StringBuilder (1024); int size = name.Capacity; if (GetUserName (name, ref size)) Output.Text = name.ToString ();
Is it possible to generate the source code for an ASP.NET Web service from a WSDL contract?
Yes. Begin by running the Wsdl.exe tool that comes with the .NET Framework SDK with a /server switch and pointing it to the WSDL document, like this:
Wsdl.exe responds by generating a CS file containing a WebService-derived base class that you can further derive from to implement a Web service. The Wsdl.exe-generated class contains abstract methods representing the Web methods described in the WSDL document; you'll need to override and implement these methods in your derived class. You must also manually copy the attributes in the Wsdl.exe-generated source code file to the corresponding elements in your source code file.
Top
When I call DataAdapter.Fill to fill a DataTable, the data comes back just fine, but any constraints placed on that data in the database do not. For example, if the table I query contains a unique key constraint, the resulting DataTable does not. If I modify the DataTable and violate a constraint, I don't learn about my mistake until I call DataAdapter.Update. Is there a reasonable way to read constraints from a database and apply them to a DataTable?
There is: it's called FillSchema. The page below demonstrates its use. The record added to the DataTable violates a uniqueness constraint because the "title_id" of the Pubs database's "titles" table is a primary key and it already contains a record with a "title_id" value of TC7777. Thanks to FillSchema, the call to Rows.Add throws an exception.
SqlDataAdapter adapter = new SqlDataAdapter ( "select * from titles", "server=localhost;database=pubs;uid=sa" ); DataSet ds = new DataSet (); adapter.Fill (ds, "Titles"); DataTable table = ds.Tables["Titles"];
adapter.FillSchema (table, SchemaType.Mapped); DataRow row = table.NewRow (); row["title_id"] = "TC7777"; row["title"] = "Programming Microsoft .NET"; row["price"] = "59.99"; row["ytd_sales"] = "1000000"; row["type"] = "business"; row["pubdate"] = "May 2002"; table.Rows.Add (row); // Get ready for an exception!
As an aside, you generally want to call FillSchema after DataAdapter.Fill, not before. Placing constraints on a DataTable before filling it slows down the query.
Top
I'm using Windows authentication in an ASP.NET intranet app to identify callers, and Windows authentication to authenticate callers to a back-end SQL Server database. However, it doesn't work: the caller's credentials are apparently not being propagated to the database. What's wrong?
I'll bet you're using Integrated Windows Authentication (IWA) to authenticate callers, and if that's the case, you've encountered the famous Windows "one-hop" problem. By default, Windows only allows security credentials to travel one hop over the network. If you're using IWA, you use up your one hop going from the browser to the Web server. Several solutions exist, but none are perfect. The best solution, assuming you want to continue using IWA, is to enable delegation. Here are some links to articles with more information: http://msdn.microsoft.com/library/default.asp?url=/library/enus/adminsql/ad_security_2gmm.asp http://msdn.microsoft.com/library/default.asp?url=/library/enus/dnnetsec/html/SecNetHT05.asp
Top
I'm using a SqlDataAdapter to update my database and a SqlCommandBuilder to generate INSERT, UPDATE, and DELETE commands. Everything works fine until I try to update a table that has spaces in its name. Then, SqlDataAdapter.Update throws a SqlException. Why does this happen, and how do I fix it?
The problem is that CommandBuilder isn't smart enough to figure out that table names with embedded spaces must be delimited with special characters such as [ and ], even if your SELECT statement contains a table name surrounded by those characters. The solution lies in CommandBuilder's QuotePrefix and QuoteSuffix properties. A CommandBuilder used this way will fail when it encounters a table whose name includes spaces:
adapter.Update (table);
But a CommandBuilder used this way will work just fine with most databases:
SqlCommandBuilder builder = new SqlCommandBuilder (adapter); builder.QuotePrefix = "["; builder.QuoteSuffix = "]"; adapter.Update (table);
Top
How can I use ASP.NET validation controls to check for leading and trailing spaces in user input and display an error message if either is present?
Here's a CustomValidator that checks a control named "Input" for leading and trailing spaces:
<asp:CustomValidator RunAt="server" ControlToValidate="Input" ClientValidationFunction="__checkLeadingAndTrailingSpaces" OnServerValidate="CheckLeadingAndTrailingSpaces" ErrorMessage="Please delete leading and trailing spaces" /> . . . <script language="JavaScript"> <!-function __checkLeadingAndTrailingSpaces (source, args) { args.IsValid = !((args.Value.charAt (0) == ' ') | (args.Value.charAt (args.Value.length - 1) == ' ')); } --> </script> <script language="C#" runat="server"> void CheckLeadingAndTrailingSpaces (Object sender, ServerValidateEventArgs e) { e.IsValid = !(e.Value.StartsWith (" ") | e.Value.EndsWith (" ")); } </script>
Top
Why do uploads fail when I use an ASP.NET file upload control to upload large files?
ASP.NET limits the size of requests (and therefore file uploads) as a precaution against denialof-service attacks. By default, ASP.NET won't accept requests whose size exceeds 4 MB. You can change that by modifying the maxRequestLength attribute of Machine.config's <httpRuntime> element. The following maxRequestLength attribute expands the permissible request size to 8 MB (8192K):
Top
Top
I upgraded my company's Web server to ASP.NET 1.1 and it broke one of my apps. Can I revert to ASP.NET 1.0 for that application and leave others running under 1.1?
You bet. Simply modify the IIS metabase to point that application to version 1.0.3705 of Aspnet_isapi.dll instead of version 1.1.4322. The easy way to do it is to run the Aspnet_regiis utility that came with version 1.0 of the .NET Framework. The following command uses Aspnet_regiis to configure the application in the virtual directory named MyApp to use ASP.NET 1.0:
If you later decide to migrate the application to ASP.NET 1.1, simply repeat the command, but this time use the Aspnet_regiis utility that came with version 1.1. For additional information, refer to the article entitled ASP.NET Side-by-Side Execution of .NET Framework 1.0 and 1.1 on the ASP.NET team's Web site.
Top
Can an ASP.NET app figure out at run-time what version of ASP.NET it's hosted by?
Yes, by reading the static Version property of the Framework's System.Environment class. Here's a page that demonstrates how. When executed, the page displays the version of ASP.NET that it's running under:
<html> <body> <h1><asp:Label ID="Output" RunAt="server" /></h1> </body> </html> <script language="C#" runat="server"> void Page_Load (Object sender, EventArgs e) { Output.Text = String.Format ("This page is using ASP.NET {0}.{1}", Environment.Version.Major, Environment.Version.Minor); } </script>
Should you need them, build and revision numbers are also present in the System.Version object returned by the property's get accessor.
Top
ASP.NET suddenly stopped working on my Web server. Every attempt to fetch a page results in a "Server Application Unavailable" error. What's wrong, and how do I fix it?
This is a symptom of a problem that occurs when you install recent Windows security updates (specifically, Security Update MS02-32 and possibly later updates, too) on Web servers running ASP.NET 1.0. It doesn't affect ASP.NET 1.1. You'll find a description of the problem and instructions for fixing it at http://www.asp.net/faq/ms03-32-issue.aspx.
Top
<html> <body> <h1>Installed HTTP Modules</h1><hr> <asp:Label ID="Output" RunAt="server" /> <body> </html> <script language="C#" runat="server"> void Page_Load (Object sender, EventArgs e) { HttpModuleCollection modules = HttpContext.Current.ApplicationInstance.Modules; string[] names = modules.AllKeys; StringBuilder builder = new StringBuilder (1024); foreach (string name in names) builder.Append (name "<br>"); Output.Text = builder.ToString (); } </script>
Top
Top
Is it possible to access session state in a custom HTTP handler? HttpContext.Session is null when a handler's ProcessRequest method is called.
To make session state accessible to an HTTP handler, derive the handler from IRequiresSessionState for read/write access to session state, or IReadOnlySessionState for read-only access. Both are "marker" interfaces that have no methods and therefore require no implementation. You'll still need, of course, to derive your handler from IHttpHandler.
Top