|
 Friday, April 13, 2007

The .NET Developer Search Engine

While listenening to a recent episode of DotNetRocks, Dan Appleman noted that he created a Google Custom Search Engine called SearchDotNet.com. This search engine searches only relevant .NET sites that Dan himself deems as "experts" in the fields. THis search engine helps with the topic of Discoverability (see the front page of SearchDotNet.com for more info on Discoverability).

Example:

He gave a good example, go to Google.com and search for "RSS".

The top 5 results are:

  1. Rss (wikipedia)
  2. Xml.com (what is Rss)
  3. Rss 2.0 Specification
  4. New York Times Rss
  5. Rdf Site Summary

As a .NET developer this poses no great info for example: how to implement Rss, or maybe .NET Rss feeds.

Enter Dan's search engine, SearchDotNet.com.

Entering the same exact search phrase returns vastly different topics (in regards to .NET).

  1. Scott Gutheries Blog feed
  2. Raymond Chen's blog
  3. Asp.net blogs
  4. Microsoft Team Rss Blog
  5. The ServerSide Rss Feed

Try searching for other topics on his site, such as the word "Triplet". Mads Kristensen blogged about this class awhile back, but searching for "Triplet" on Google.com would return you all kinds of non-.NET related articles.

Dan recommended that the developer try to search on SearchDotNet.com first, then if you cannot find what you're looking for, then go search on Google.com. Thats what I've done and found it immensely useful.

See for yourself, go try it out now.

kick it on DotNetKicks.com
#    Comments [0] |
 Thursday, April 12, 2007

Codeplex is down?

This morning, I went to log into Codeplex and I got the dreaded YSOD. You'd think that Microsoft would have this under wraps. With all of the redundancy and technology that Microsoft has at its fingertips, you'd think this wouldnt be an issue. Sometimes it's great to know, that even the big guys make mistakes. :)  

#    Comments [0] |
 Thursday, April 05, 2007

Keyboard Shortcuts for TestDriven.NET

I'm constantly clicking "Repeat Test Run" or "Run Tests" inside of of the VS 2005 IDE. Yes, I'm willing to admit that I'm a firm believer in going commando when it comes to the mouse. Its just, well, too time consuming. On average, I would normally right-click "Repeat Test Run" or "Run Tests" hundreds of times. This got real old. So I created a keyboard shortcut in Visual Studio.

Some people see me use this and have asked how to do this. Unfortunatley I thought this was a basic thing to know, but apparently its not (at least from the circles that I've run with).

So here's how to do it.

In Visual Studio, go to Tools, Options

Then go to Environment, Keyboard.

Type in "TestDriven" into the Commands Containing window. Select "TestDriven.NET.RunTests" command. Then place your cursor into the "Presss shortcut keys" textbox. Then hit the follow keys: Hold down the CTRL button and then press (while holding down CTRL) R and then release R and then press T (while still HOLDING the CTRL button). This will produce: CTRL + R, CTRL + T. Click assign, and OK and you're done!  

 

Now, inside of your test code, method or class, hit the keyboard shortcut. Hold down CTRL while pressing R and then T. CTRL + R, CTRL + T.

The test will run. You can also apply this to other items such as "Repeat Test Run" and many others. :)

kick it on DotNetKicks.com
#    Comments [0] |
 Sunday, April 01, 2007

Handling unexpected exceptions and tracing

I was browsing a site earlier and I came upon the dreaded Yellow Screen of Death (YSOD). It reminded me of when I had to implement some diagnostic tracing for an application that happened to be built prior to my time at the said company.

The Problem

The application was written in ASP.NET 1.1 and was recently upgraded to 2.0. There was a TON of code in the code behind, heck, we could go as far as to say that the majority of the business logic was in the code behind. The validation and error checking routines were nearly non-existant and as of lately the system was encountering a lot of YSOD screens. The users were confused and on top of that the YSOD screen was presenting TOO MUCH info, enabling a malicious user to take that info and use it for evil purposes. I HAD to get rid of these errors, and quick, and I wasnt allowed to turn off the application so I could fix it. I was allowed to take down the app for a few seconds to implement a change and thats it. I had the error report but could not

I had to implement some way of tracking these bugs without taking down the application for good. I did this through the Application_Error event in the Global.asax file.

Background Info on Page Processing

When a ASP.NET Page is processing and it encounters an error, and if it is not handled, it will present the YSOD with all the pertinent information to the user (that is, if no error handling or default error page is defined in the web.Config). In this project, that was the case. No error handling was present.

An ASP.NET page can also catch its own errors if you implement the Page_Error method. Inside of this method you can get the last error that was thrown and do something with it and then move on. Like this:

public void Page_Error(object sender, EventArgs e)
{
   Exception ex = Server.GetLastError();
   /// Do some stuff with the exception, such as logging, etc 

   // Clean  up 
   Server.ClearError();
}

The problem with this method is that you must implement this on every page. You could do this if had a base page class you inherited from, but what if you forgot to inherit from your base page? Your page would still blow up when something went wrong and was not handled.

Enter Global.asax and Application_Error ...

Implementing Application Level Error Handling & Logging

You can implement application level error handling by implementing some handling at the Application level.

Here's some code, explanation is at the bottom.


Global.asax File

<%@ Application Language="C#" %>
<%@ Import Namespace="System.Web.Configuration" %>

<script runat="server">

void Application_Start(object sender, EventArgs e)
{
}

void Application_End(object sender, EventArgs e)
{
}

void Application_Error(object sender, EventArgs e)
{
   // Perform some handling here if necessary, and then redirect to error handling page as defined in the web.Config file.
   CustomErrorsSection section = (CustomErrorsSection)ConfigurationManager.GetSection("system.web/customErrors");
   Server.Transfer(section.DefaultRedirect);
}

void Session_Start(object sender, EventArgs e)
{
}

void Session_End(object sender, EventArgs e)
{
}

</script>


web.Config File

<?xml version="1.0"?>
<configuration>
    <appSettings/>
    <connectionStrings/>
    <system.web>
         <compilation debug="true"/>
         <authentication mode="Windows"/>
         <customErrors defaultRedirect="Oops.aspx" />
         <trace writeToDiagnosticsTrace="true" enabled="true"/>
    </system.web>
    <system.diagnostics>
        <trace autoflush="true">
            <listeners>
               <add name="TraceSiteEventLogListener" type="System.Diagnostics.EventLogTraceListener,System,version=2.0.0.0,Culture=neutral,PublicKeyToken=b77a5c561934e089" initializeData="Test123"/>
            </listeners>
        </trace>
    </system.diagnostics>
</configuration>


Oops.aspx Page

using System;
using System.Data;
using System.Configuration;
using System.Collections;
using System.Web;
using System.Web.Security;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using System.Web.UI.HtmlControls;
using System.Web.Configuration;

public partial class Oops : System.Web.UI.Page
{
   protected void Page_Load(object sender, EventArgs e)
   {
      Exception ex = Server.GetLastError();
      string errorInfo = GetFormattedException(ex); 
      Trace.Write(errorInfo);
      Server.ClearError(); 
   }

   /// <summary>
   /// Gets the exception information, recursively. 
   /// </summary>
   /// <param name="ex">The exception that occurred.</param>
   /// <returns>A formatted string that contains the message, exception type, source and stack trace for each exception
   /// that exists within the exception hierarchy.</returns>
   private string GetFormattedException(Exception ex)
   {
      string exceptionString = string.Empty;
      // If we have an inner exception, we need to get that information first. 
      if (ex.InnerException != null)
      {
         exceptionString = String.Format("Inner Exception: {0}{1}", GetFormattedException(ex.InnerException), Environment.NewLine);
      }
      string exceptionFormat = "[{0}] - Message: {1} - Exception Type: {2} - Source: {3} - Stack Trace - {4}";
   
      exceptionString += String.Format(exceptionFormat,
            DateTime.Now,
            ex.Message,
            ex.GetType().FullName,
            ex.Source,
            ex.StackTrace);

      return exceptionString;
   }


}



Oops.aspx

<%@ Page Language="C#" AutoEventWireup="true" CodeFile="Oops.aspx.cs" Inherits="Oops" %>

<!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 runat="server">
      <title>Error Page</title>
   </head>
<body>
   <form id="form1" runat="server">
   <div>
      Oops! An error occurred!
   </div>
   </form>
</body>
</html>



Explanation

If an error is thrown, on any page, the Global.asax's Application_Error event catches the error. At that time I redirect the user to the default error page that is defined in the web.Config's customErrors section (Oops.aspx).

In that file (Oops.aspx) I grab the last exception by making a call to Server.GetLastError(). Then I recursively loop through each child exception that the exception has and format the data for viewing purposes.

After the data is formatted I then write it to the Page.Trace, which is set up to write it to the Trace Listeners that are set up in the web.config. I have decided to use the Event Log as my location of writing my trace information to, but this could easily be set up to use the TextWriterTraceListener (or any other listener) as well.

Then at the end, I clean up the exception queue by calling Server.ClearError().

Then, the page is displayed. "Oops! An error occurred!".

Now, open up the event viewer and look inside of the application log. You'll see a source by the name of "Test123" and you'll see the error information inside. You'll also find a plethora of other great event information too.  

For this to all work, we have to set the Trace enabled to true in the web.Config and set the writeToDiagnosticsTrace equal to true as well. Otherwise the Page's Trace will not redirect its trace information to the listeners defined in the web.Config. To turn this off, just set the enabled flag to "false".

Conclusion

The best thing to do is to always capture the error, give the user a meaningfull message such as "First name cannot be blank." But at times there are errors that we don't catch as developers, things slip through the cracks and we miss something, that's life, it happens. In event of such case, we can enable global exception handling, just like we did here. Just in case something "exceptional" happens, we can catch it, log it in the event viewer and not have to worry about displaying vital information to a user through the YSOD.

 

#    Comments [0] |
 Thursday, March 29, 2007

ASP.NET HOWTO: Enable Default Enter Button in ASP.NET 2.0

In certain forms, such as search forms, users enter a value into the search query box, then press the enter button. If you are develolping in ASP.NET, the page will refresh, but nothing will happen. The buttons click event wont fire... UNLESS, you explicity enable a default enter button on the form.

A problem that existed in ASP.NET 1.x was that you could not specify a default enter button unless you provided some custom code. In ASP.NET 2.0 you can do this very easily, unfortunately its not that widely used, much less known.

HOW TO:

The HtmlForm object has a property DefaultButton. This property gets or sets the control that that causes the post back when the ENTER key is pressed.

Example

<body>
   <form id="form1" runat="server" defaultbutton="processRequestButton">
      <div>
         <asp:TextBox AccessKey="c" runat="server" ID="somevalueTextbox"></asp:TextBox>
         <asp:Button runat="server" ID="processRequestButton" Text="Process" OnClick="processRequestButton_Click" />
      </div>
   </form>
</body>

Result

When the user presses the ENTER key, the form will be posted back and the processRequestButton_Click event will be fired.

Simple!

kick it on DotNetKicks.com
#    Comments [4] |