Archive for October, 2006

Changing Login control defaults

If your application uses forms-based authentication and unauthenticated users try to access a protected page, then they’ll be redirected automatically to a login page. By default this is Login.aspx. However, you can specify a different login page by setting the loginUrl attribute in the forms section of your web.config, shown here:

<system.web>
  <authentication mode="Forms">
    <forms loginUrl="~/customLogin.aspx"/>
  </authentication>
</system.web>

Once logged in successfully, users will be redirected to Default.aspx. You can specify a different page using the DestinationPageUrl property of the Login control:

<asp:Login id="Login1" runat="server" DestinationPageUrl="welcome.aspx"/>

You can also set this in web.config using the defaultUrl attribute:

<system.web>
  <authentication mode="Forms">
    <forms defaultUrl="~/welcome.aspx"/>
  </authentication>
</system.web>

Lightweight databases for .NET

Need a nice lightweight database with an ADO.NET Data Provider? Try one of these:

ArrayList vs. List<Object>

The List class, introduced in .NET 2.0, is the generic equivalent of the ArrayList class. Both are functionally very similar and will store a list of Objects, and thanks to polymorphism, anything that derives from the Object class. That is to say, any object at all.

So what’s the difference, and why would you use one in preference over the other? I think it’s a question of clarity and good programming style. If your intention is to store a list of Objects (or derivatives) then you can directly imply this by using a List<Object> type. If you want to store some other type then you would use the correct List type parameter instead of Object, right? Such as List<String> or List<XmlDocument>.

With ArrayLists there’s no way to make this distinction, which makes your code more vulnerable to misinterpretation.

No Silver Bullet

No Silver Bullet is an interesting and insightful essay on software engineering. The author, Fred Brooks, argues that we’re now converging towards a limit in productivity that cannot be exceeded by simply applying new technologies or development practices.

His reasoning is that technology is now sufficiently advanced that there are no more major productivity gains to be made by fixing avoidable issues in software development, such as abstracting complexity using higher level languages.

We’ve now reached the point, he says, where the main challenge is to understand and model accurately the inherent essence of a programming problem. This might involve the complex interplay of several discrete components, each performing their own tasks while depending on, and feeding back into, other parts of the system. Furthermore, adding new elements to such a system can increase the complexity geometrically. No new development methodology or software technology will remedy these issues significantly.

Free ASP.NET CMS

There are some great commercial .NET content management systems around these days, like Sitecore and Immediacy, but if you need something free then try one of these:

Please leave a comment if you can recommend any others.

Parsing HTML in .NET

Parsing XML in .NET is well supported by the Framework’s XmlDocument and XPathDocument classes, but unfortuantely there’s no similar built-in provision for parsing HTML. If you’ve ever tried to parse HTML by hand, maybe using string manipulation or regular expressions, then you’ll know just how frustrating and difficult it can be.

Luckily, Simon Mourier’s excellent Html Agility Pack solves this problem. It’s one of the most useful .NET libraries I’ve come across, and will parse even the sloppiest HTML into an orderly structure of objects, similar to the XML DOM.

The pack is bundled with full source C# code, API documentation, and samples showing how to convert HTML into plain text, RSS and XML.

Here’s a short code example to show it in action:

HtmlWeb htmlWeb = new HtmlWeb();
HtmlDocument doc = htmlWeb.Load("http://www.bbc.co.uk/");
 
HtmlNodeCollection links =
    doc.DocumentNode.SelectNodes("//a[@href]");
 
foreach (HtmlNode link in links)
{
    Response.Write(link.Attributes["href"].Value + "<br>");
}

There are loads of great applications for HTML parsing, here are just a few:

  • Fixing malformed, deprecated or inaccessible HTML
  • Extracting links, images or other resources from a web page
  • Extracting content from a page for importing into a CMS
  • Transforming HTML into other formats, such as RSS
  • Building mashups

Learning ASP.NET

Everyone has to start somewhere, so here are some great resources for beginners wanting to learn ASP.NET:

Online Resources

Free Software

Books

Communities

C# equivalent of VB’s IsNumeric()

Introduction

Unlike VB 2005, C# seems to be missing a function for checking whether a string contains a value that can be evaluated as a number. The post will demonstrate some C# alternatives to VB’s IsNumeric() function.

Int32.Parse()

First up is the Int32.Parse() method. It’s fairly straightforward, you pass it a string parameter and it tries to parse it as an integer. If the string can’t be converted to an int then the method fails and an exception is thrown:

try
{
    int result = int.Parse("123");
    Debug.WriteLine("Valid integer: " + result);
}
catch
{
    Debug.WriteLine("Not a valid integer");
}

Here we’re using the int data type which is just a C# synonym for Int32. It’s a 32-bit integer so will successfully parse any whole number between –2147483648 and 2147483647 inclusive. The Parse() method is also available for other integral types, floating point types and decimals.

This is a good solution, but it’s quite verbose and includes the overhead of throwing an exception if the conversion fails.

Int32.TryParse()

The Int32.TryParse() method is a .NET 2.0 refinement of Int32.Parse(). It’s more succinct and doesn’t throw an exception if parsing fails. Here’s how it works:

int result;
if (int.TryParse("123", out result))
{
    Debug.WriteLine("Valid integer: " + result);
}
else
{
    Debug.WriteLine("Not a valid integer");
}

Convert.ToInt32()

The Convert.ToInt32() method is very similar to Int32.Parse() and will throw an exception if the string argument is not a valid Int32 value. The only difference is that Convert.ToInt32() will accept a null argument and evauluate it as zero, whereas Int32.Parse() will throw an ArgumentNullException, i.e.

// throws ArgumentNullException
int result1 = Int32.Parse(null);
 
// doesn't throw an exception, returns 0
int result2 = Convert.ToInt32(null);

IsNumeric()

That’s right, you can actually call VB’s IsNumeric() function from C#. First add a reference to the Visual Basic compatibility assembly, Microsoft.VisualBasic.dll, which contains the function’s implementation:

You can now access any of the methods from the Microsoft.VisualBasic.Information class, including IsNumeric(), like this:

using Microsoft.VisualBasic;// ......bool result = Information.IsNumeric("123");

This isn’t really a recommended approach because these classes were included in .NET to provide backward compatibility with legacy VB code.

Pattern Matching

Pattern matching using a regular expression is a very flexible solution, and is especially useful if you need to test for very large numbers that might be too big to fit in a variable.

string strToTest = "123";
Regex reNum = new Regex(@"^\d+$");
bool isNumeric = reNum.Match(strToTest).Success;

This pattern will evaluate true for any non-negative integer. You could adapt the regular expression to include any number format you want, such as negative, decimal or even complex numbers.

Char.IsNumber() and Char.IsDigit()

Finally, I’ll just mention two methods of the char class, Char.IsNumber() and Char.IsDigit(). These can be used to check whether a single character is a number, e.g.

bool isNumeric = Char.IsNumber('5');  // true

The differences between these two methods are quite subtle, so read the documentation carefully to make sure you’re using the right one.

Summary

To borrow a phrase from Perl, There’s More Than One Way To Do It (TIMTOWTDI). Wrap any of these techniques in a method and you’ve got your very own C# IsNumeric() function.

Quick Warning

One final word of warning, be careful with code like this:

string str = "\x1811\x1812\x1813";
Regex reNum = new Regex(@"^\d+$");
if (reNum.Match(str).Success) // evaluates to true
{
    int i = int.Parse(str); // throws FormatException
}

Your regular expression \d digit character will match any Unicode digit, even if, as shown here, it’s Mongolian. The Int32.Parse() method isn’t quite as pragmatic and will throw a FormatException.

Sending dynamically generated files to the browser in ASP.NET

Intro

Let’s suppose that you’ve got some data in your ASP.NET page, and you want to send it to your users as a file that they can download to their desktop. It doesn’t really matter what type of data, it could be a Microsoft Word document, a JPEG image or maybe just some plain boring text. It might’ve been loaded from a file somewhere, or generated dynamically inside the page.

In this example we’ll use some XML because it’s nice and simple. Maybe it’s your company’s monthly sales figures and users need to download the data to generate reports.

Setting the HTTP headers

You’ve got your data, in this case encapsulated in an XmlDocument object, and you’re ready to send it to the browser. The first step is to tell the browser what kind of data you’re going to be sending by setting the “content-type” HTTP header to “text/xml”. Usually this is set to “text/html” for regular HTML content, but you can use whichever MIME type matches your data.

Next, tell the browser not to display the data, but instead prompt the user with a file download dialog. You also need to give your file a name, otherwise the filename of your .aspx page will be used by default. Both these things are achieved by adding another HTTP response header, “content-disposition”.

Finally, send your data to browser. Here’s some example code to illustrate:

XmlDocument xmlDoc = new XmlDocument();
// (build your XML document here)

Response.AppendHeader("content-type", "text/xml");
Response.AppendHeader("content-disposition", "attachment;filename=data.xml");

Response.Write(xmlDoc.OuterXml);
Response.End();

Note the use of AppendHeader to set your page response’s HTTP header information.

You should be presented with a file download dialog to save your XML file:

Summary

This technique can be used for all sorts of things, among the most useful are probably serving downloadable reports to your users, and providing protected access to files outside the webroot.