navigation
 Monday, June 05, 2006

A lot has been written about the merits of multiple narrow indexes versus single larger compound indexes. This blog by Grant Fritchey yields juicy food for thought on that very topic, backed by some very thorough testing with real-world data.

posted on June 5, 2006  #    by Adam Anderson  Comments [0] Trackback
 Wednesday, May 31, 2006

There is a cool new feature in ASP.NET 2.0 that allows you to embed resources into your server side assemblies and have your web controls request these resources automatically. This is especially handy when you want to deploy a new control. No longer do you have to find just the right spot to place the JavaScript and image files that your control depends on. 

Microsoft already uses this technique with its validators. The JavaScript needed for client side validation is embedded in one of the .NET assemblies on the server. If you were to view the source of any web page that uses a validator in ASP.NET 2.0, you might see something like this:

<script src="/MyWebSite/WebResource.axd?d=68d8KT0ikvX6J4c8Z8zwvfZXzPdyyYYY8TuccizWlCePFWz&t=632847500868593750" type="text/javascript"></script>

This is a request to a new HttpHandler, WebResources.axd, for the necessary JavaScript. The first parameter, ‘d’ is an encrypted identifier telling the handler which resource it needs. The ‘t’ parameter is an encrypted timestamp, used to determine if the resource has changed.

Looks pretty simple right?

Well, lets see how easy it is for you to use this technique. Lets try to create a control that has its JavaScript embedded as a resource. For this example, I am going to use a useful script that buffers key presses and matches an entire word in a dropdownlist instead of just the first character. (Thanks to Jonathan Cogley at ASP Alliance for the original script: http://authors.aspalliance.com/thycotic/articles/view.aspx?id=3)

First, lets open up or create a new web site in Visual Studio 2005 and then add a new Web Control Library project to it. After adding the project, remember to add a reference to it from our web site.  This new assembly will contain all of the elements of our control and our web site has to know about it to use it.

Next, lets add a new class that inherits from DropDownList:

using System;
using System.Collections.Generic;
using System.Text;
using System.Web.UI.WebControls;
using System.Web.UI;


 

namespace WebControlLibrary1
{
    public class KeyedDropDownList : DropDownList
{
        private bool _caseSensitive = false;

        public KeyedDropDownList(){}

    
     public bool CaseSensitive
     {
     get
     {
     return _caseSensitive;
     }
     set
     {
     _caseSensitive = value;
     }
     }
    }
}

I have also added a property “CaseSensitive” which will be passed to the script to allow us to turn on and off case sensitivity when matching strings in our list.

Now add a new a JScript file and examine the properties. To embed this file as a resource the Build Action must be set to 'Embedded Resource'.  After setting the build action,  feel free to copy this code into the file or substitute JavaScript of your own:

function KeyedDropDownList_onkeypress(dropdownlist,caseSensitive) 
{
// check the keypressBuffer attribute is defined on the dropdownlist
var undefined;
if (dropdownlist.keypressBuffer == undefined)
   {
dropdownlist.keypressBuffer = '';
}
   // get the key that was pressed
var key = String.fromCharCode(window.event.keyCode);
dropdownlist.keypressBuffer += key;
  if (!caseSensitive)
   {
// convert buffer to lowercase
dropdownlist.keypressBuffer = dropdownlist.keypressBuffer.toLowerCase();
}
  // find if it is the start of any of the options
var optionsLength = dropdownlist.options.length;
for (var n=0; n < optionsLength; n++)
{
var optionText = dropdownlist.options[n].text;
if (!caseSensitive)
     {
optionText = optionText.toLowerCase();
  }
if (optionText.indexOf(dropdownlist.keypressBuffer,0) == 0)
     {
dropdownlist.selectedIndex = n;
return false; // cancel the default behavior since
// we have selected our own value
     }
}
// reset initial key to be inline with default behavior
dropdownlist.keypressBuffer = key;
return true; // give default behavior
}

Next,  you must add an attribute to the assemblyinfo.cs file to reference the embedded resource:

[assembly: System.Web.UI.WebResource("WebControlLibrary1.JScript1.js", 
"text/javascript")]

You will notice that the reference to the JScript file is fully qualified. You must do this using the project's default namespace, not just any old namespace in the assembly. You can see this  by right clicking on the project and viewing the properties. I should also mention that we intentionally left out an optional boolean parameter named 'PerformSubstitution'. By default 'PerformSubstitution' is off, and you should turn it on if the referenced resource accesses other embedded resources. An embedded style sheet that defines some style using an embedded image is a good example of when to set PerformSubstitution to true.

Now, instead of registering the script block with the necessary JavaScript ourselves, we will register a request to get the script block from our  assembly.  Thanks to the new ClientScriptManager in ASP.NET 2.0, this is  very easy.  We will first override the OnInit method of our KeyedDropDownList class and then  get a reference to this manager to do most of our work.

protected override void OnInit(EventArgs e)    
{
base.OnInit(e);
ClientScriptManager cs = Page.ClientScript;
Type rsType = this.GetType();
cs.RegisterClientScriptInclude("MyScript",
cs.GetWebResourceUrl(rsType,"WebControlLibrary1.JScript1.js"));
}

The last piece of coding is very simple.  Just attach  the onkeypress event of our control to our custom JavaScript function.  Although in this example we have not used the ViewState to track our lone property,  we might want to do that or add more properties that can be passed to the JavaScript function.  So here I overrode the OnLoad method to add the attribute to our webcontrol. By the time the OnLoad event is fired, the ViewState will have been  loaded.

protected override void OnLoad(EventArgs e)
{
base.OnLoad(e);
this.Attributes.Add("onkeypress",
"return KeyedDropDownList_onkeypress(this," +
_caseSensitive.ToString().ToLower() + ")");
}

If all goes well, we should be able to now use this control on any form in our web app and the necessary JavaScript will automatically accompany it wherever it goes. I should also point out that when debugging, the JavaScript is not cached, it is downloaded each time. When debugging is off, the JavaScript is cached on the client automatically. 

I have  included the markup for the test page that registers and uses this new control below.

<%@ Page Language="VB" AutoEventWireup="false"
CodeFile="Default.aspx.vb" Inherits="_Default" %>
<%@ Register Assembly="WebControlLibrary1"
Namespace="WebControlLibrary1" TagPrefix="wcl" %>
<html xmlns="http://www.w3.org/1999/xhtml">
<head id="Head1" runat="server">
<title>Test Page</title>
</head>
<body>
<form id="form1" runat="server">
<wcl:KeyedDropDownList id="ddlTest" runat="server">
<asp:ListItem Value="1">Calendar</asp:ListItem>
<asp:ListItem Value="2">Can</asp:ListItem>
<asp:ListItem Value="3">Candle</asp:ListItem>
<asp:ListItem Value="4">Canary</asp:ListItem>
<asp:ListItem Value="5">Card</asp:ListItem>
<asp:ListItem Value="6">Cat</asp:ListItem>
<asp:ListItem Value="7">Cow</asp:ListItem>
</wcl:KeyedDropDownList>
</form>
</body>
</html>
posted on May 31, 2006  #    by Mike Dugan  Comments [0] Trackback
 Tuesday, May 30, 2006

I encountered a troubling message while playing around with SQL Server 2005 and its new Integration Services. It read, “Failed to save package file "C:\Documents and Settings\Adam Anderson\Local Settings\Temp\tmp44F.tmp" with error 0x80029C4A "Error loading type library/DLL.".”

I first saw the error when I tried to create an Integration Services project in Visual Studio 2005, but the project was created anyway. I opened the project and ignored the error, hoping it was merely a glitch. However, after setting up a data source, I tried to add a package, and was blocked by the same error message. Now I knew I was in trouble. This was a brand-new installation, and it was annoying to see such an uninformative message, which is usually a sign that something has gone wrong at a fundamental level. Fortunately, my situation was not unique, and a search on Google quickly brought me to the correct solution.

It turns out that the MSXML 6.0 Parser was either incompletely installed or the installation was damaged.

To fix:

  1. Start Menu | Control Panel | Add or Remove Programs
  2. Locate MSXML 6.0 Parser in the list
  3. Click Change. The Setup dialog will appear.
  4. Click Next.
  5. There will be two options: Repair and Remove. Select Repair, then click Next.
  6. The Setup dialog will display a progress bar and then (hopefully) report success.
posted on May 30, 2006  #    by Adam Anderson  Comments [1] Trackback
 Monday, April 24, 2006

While browsing the web today, I came across a recommendation for Foxit Reader as an alternative to the more well-known Adobe Acrobat Reader. After downloading it and trying it out, I am happy to report that my first impressions of the product are very positive. Some of my biggest causes for discontent with Adobe Acrobat were the sluggish loading times and persistent nagging to upgrade, and both of these are a thing of the past with Foxit. Now when I double-click a PDF file, it opens in the blink of an eye! Farewell, Acrobat. Your slowness and nagging won't be missed.

posted on April 24, 2006  #    by Adam Anderson  Comments [0] Trackback
 Wednesday, April 05, 2006

The SQL Server functions ISNULL and COALESCE seem to occupy the same space in terms of functionality, only ISNULL is restricted to only two parameters, while COALESCE can take any number of parameters. So why ever use ISNULL? Answers and trivia lie within!

First of all, the simple answer for why to prefer ISNULL over COALESCE when given the choice is that ISNULL tends to produce query plans that are more efficient than COALESCE's. Examine the query plans for the two queries given below to see the difference:

-- These two yield different execution plans. 
select a.au_id,
isnull( (select price from titles where title_id = ta.title_id), 0 )
from authors a
join titleauthor ta on a.au_id = ta.au_id
select a.au_id,
coalesce( (select price from titles where title_id = ta.title_id), 0 )
from authors a
join titleauthor ta on a.au_id = ta.au_id

The first query uses one less nested loop than the second, resulting in a lower overall cost. I wouldn't recommend writing queries that nest subqueries within functions anyway, but the example was only intended to show the difference in generated execution plans, not to recommend a certain T-SQL coding style.

The other bit of trivia most people don't know about ISNULL and COALESCE is that the return data type for ISNULL is guaranteed to be the same as the data type of the first parameter. However, the return data type of COALESCE is determined by data type precedence rules (see the Books Online topic "Data Type Precedence"). Therefore, the following queries will produce different output:

declare @Example char(2) 
set @Example = null
select isnull( @Example, 'abcde' ), coalesce( @Example, 'abcde' )
The first expression using ISNULL will return 'ab', which is the declared datatype of the first parameter, a char(2). The second expression using COALESCE will return the highest precedence data type, which is the longer string 'abcde.'

Take-home lessons:
  1. When you only need to coalesce two arguments, use ISNULL instead.
  2. When using COALESCE, use explicit casting to ensure you get a consistent return data type and also to make the meaning of the code clear to those who are less knowledgeable about the quirks of ISNULL and COALESCE than you are now. ;)
posted on April 5, 2006  #    by Adam Anderson  Comments [0] Trackback
 Friday, March 31, 2006

In .NET 1.1 we always had a problem using the built in Mail class in the System.Net namespace whenever the mail server was on a different server than the authenticating web server because it lacked the authentication feature to the SMTP that allows for this to be transfered correctly.  So we always ended up using a 3rd party component like ASPNETEMAIL which worked pretty well for us.
With the release of .NET 2.0 obviously Microsoft worked on that feature and added the authentication feature to the Mail class. 
This is simple code to show how easy now it is to make this work in .NET 2.0

1 System.Net.Mail.MailMessage Email = new System.Net.Mail.MailMessage ("spamMe@falafel.com", "SpamThis@falafel.com");
2 Email.Subject = "test subject";
3 Email.Body = "this is a test";
4 System.Net.Mail.SmtpClient mailClient = new System.Net.Mail.SmtpClient();
5 //This object stores the authentication values
6 System.Net.NetworkCredential basicAuthenticationInfo = new System.Net.NetworkCredential ("username", "password");
7 mailClient.Host = "mail.falafel.com";
8 mailClient.UseDefaultCredentials = false;
9 mailClient.Credentials = basicAuthenticationInfo;
10 mailClient.Send(Email);
posted on March 31, 2006  #    by Lino Tadros  Comments [0] Trackback

When paging just won't cut it, scrollbars come in handy. Fortunately, it is quite easy to add scrolling support to just about anything. All you need to do is wrap any of your controls in an HTML DIV tag.

The key to the code below is to set the DIV style to "OVERFLOW: auto;" and specify a size. When overflow is set to auto, scrollbars will automatically appear if  the control size exceeds the DIV's dimensions. The code below binds a datagird to the Order table in the Northwind database. This would normally produces a pretty large datagrid, but thanks to our DIV, we will only see a subsection of the data.

<DIV style="OVERFLOW: auto; WIDTH: 550px; HEIGHT: 200px">
<asp:datagrid id=DataGrid1 runat="server" DataSource="<%# dataView1 %>"
AutoGenerateColumns="False" ShowHeader="False">
<Columns>
<asp:BoundColumn DataField="OrderID"></asp:BoundColumn>
<asp:BoundColumn DataField="ShippedDate"></asp:BoundColumn>
<asp:BoundColumn DataField="ShipName"></asp:BoundColumn>
<asp:BoundColumn DataField="ShipCity"></asp:BoundColumn>
</Columns>
</asp:datagrid>
</DIV>
posted on March 31, 2006  #    by Mike Dugan  Comments [0] Trackback
 Wednesday, March 29, 2006

Given an array of bytes containing the file data, a MIME type, and a file name:

public void DownloadBytes( byte[] bytes, string mimeType, string fileName )
{
  Response.ContentType = mimeType;
  Response.AppendHeader( "Content-Disposition", "attachment;filename=" + fileName );
  Response.BinaryWrite( bytes );
  Response.Flush();
  Response.End();
}

posted on March 29, 2006  #    by Adam Anderson  Comments [0] Trackback
 Thursday, March 23, 2006

The .NET Framework offers two methods for rounding: Decimal.Round() and Math.Round(). However, both methods implement a type of rounding known as "banker's rounding," which rounds .5 to the nearest even number. This is a different implementation from the "standard" arithmetic rounding most people are familiar with, where .5 rounds "up." Even the definition of "up" can vary, depending on whether the rounding is symmetric or asymmetric. Symmetric rounding rounds in the same direction relative to 0 (either towards or away). Asymmetric rounding always rounds in the same absolute direction (towards positive infinity or negative infinity).

Banker's rounding has its benefits, but it can cause incompatibility with other systems that implement different rounding schemes. For example, Math.Round( 8.5m ) will round to 8 in .NET, but round( 8.5, 2 ) will round to 9 in T-SQL. Inside is source code that implements arithmetic symmetric rounding in .NET, the type of rounding that T-SQL uses.

See How To Implement Custom Rounding Procedures for additional information

/// <summary>
/// Implements alternate rounding methods, in contrast to Decimal.Round()
/// and Math.Round(), which implement banker's rounding (5 rounds to even).
/// See http://support.microsoft.com/?kbid=196652 for other methods.
/// </summary>
public static class Round
{
  #region Methods

  #region ArithSym
  /// <summary>
  /// Rounds using arithmetic (5 rounds up) symmetrical (up is away from zero) rounding
  /// </summary>
  /// <param name="d">A Decimal number to be rounded.</param>
  /// <param name="decimals">The number of significant fractional digits (precision) in the return value.</param>
  /// <returns>The number nearest d with precision equal to decimals. If d is halfway between two numbers, then the nearest whole number away from zero is returned.</returns>
  public static decimal ArithSym( decimal d, int decimals )
  {
    decimal factor = Convert.ToDecimal( Math.Pow( 10, decimals ) );
    int sign = Math.Sign( d );
    return Decimal.Truncate( d * factor + 0.5m * sign ) / factor;
  }
  #endregion ArithSym

  #endregion Methods
}

posted on March 23, 2006  #    by Adam Anderson  Comments [0] Trackback