navigation
 Saturday, February 18, 2006

Krugle is a search engine for source code that is supposed to make life easier for us developers. You can sign up for the Beta on their website and try it out sometime next month.  http://www.krugle.com/

posted on February 18, 2006  #    by Mike Dugan  Comments [0]
 Monday, February 06, 2006

Is Courier New just not doing it for you anymore? This page summarizes and rates different fixed-width fonts based on their suitability for use in a programming environment. The reviewer evaluates several factors including how easily the characters 'l', '1', and 'i' are distinguised, as well as the characters '0', 'o', and 'O'. After browsing several of the handy font previews, simply follow the associated link to download your new favorite programming font. The top-rated font, Bitstream Sans Vera Mono, looks great without ClearType and awful with it, so if you have font smoothing enabled, I recommend you look further down the list for something that plays better with ClearType

posted on February 6, 2006  #    by Adam Anderson  Comments [0]
 Friday, January 27, 2006

Being able to compare the results of two different SQL queries and verify that they match 100% is a useful ability for any database application developer. I've encountered two main reasons for wanting to compare query results in the course of development:

  1. After importing data from an older system, verifying that equivalent reports in the old system and the new one agree
  2. When refactoring a complex query, to verify that the changes made haven't altered the output

In both cases, I find it convenient to run data comparisons of queries run in SQL Server Query Analyzer by using MS Excel. The technique is simple, but the payoff is huge: 100% confidence that every row and every column matches. Here are the steps I use:

  1. Create a new 3-page workbook and rename the tabs "Old", "New", and "Compare" 
  2. Hand-enter column names in the top row of each worksheet. I like to make them bold and underlined so Excel can tell that they're headers. 
     
  3. Run the "old" query in Query Analyzer. Once the results come back, click in the result pane and select all using Ctrl+A or Edit | Select All
  4. In the "Old" worksheet, select the leftmost cell in the row under the header and paste the results.
  5. Repeat the same steps for the "New" query and worksheet.
  6. If the queries don't explicitly set their own sort order, I recommend sorting both the "Old" and "New" data in a way that should guarantee that they are ordered the same on the two worksheets.
  7. In the "Compare" worksheet, enter the following formula in the leftmost cell under the header row: =Old!A2=New!A2. This formula will return TRUE if the cells match, and FALSE if they don't. 
  8. To make mismatches easier to spot, I like to apply some conditional formatting as well.

    1. Select the cell and then select Format | Conditional Formatting...
    2. Set up the condition so that it reads "Cell Value Is" "equal to" "FALSE"
    3. Click the Format... button. On the Patterns tab, select a nice bright color that will stand out, like red. Click OK.
    4. Click OK on the Conditional Formatting dialog
  9. Select the span from this initial cell to the rightmost column in the result set and press Ctrl+R or Edit | Fill | Right.
  10. Check the "Old" and "New" worksheets and note the number of the last row containing query results.
  11. In the "Compare" worksheet, select all the cells that contain data in the same row and column on the other two sheets and press Ctrl+D or Edit | Fill | Down.
  12. Differences in data will result in a value of FALSE (optionally highlighted if you used conditional formatting). The values can be scanned for visually, or you can use the Find dialog to search for them. When using the Find dialog, make sure to click the Options button and change "Look in" from "Formulas" to "Values" 
  13. Once differences are found, the old and new data can be examined to determine the cause for the difference.
posted on January 27, 2006  #    by Adam Anderson  Comments [0]

The MSDN Library says this about the DataGrid.Items property:

"Only items bound to the data source are contained in the Items collection. The header, footer, and separator are not included in the collection."

So how do we get to these other items? Most people handle the ItemCommandEvent for the grid, but there is a way to access them directly.

If these items are not in the DataGrid.Items collection, then where are they? To find out, turn on tracing for your ASP.NET webpage. You will see that the grid is rendered something like this:

DataGrid
   DataGridTable
      DataGridItem
         TableCell
         TableCell
      DataGridItem
         TableCell
         LiteralControl
         Label
      DataGridItem
      ...

What we are seeing is that the first object in the DataGrid's Control hierarchy is a DataGridTable. That DataGridTable contains ALL of the DataGridItems, including the header and footer. To get these DataGridItems we just need to grab the first or last control out of the DataGridTable's control collection.

So to get a DataGrid's footer, this code will do this trick:

//First get the DataGridTable (the first control in DataGrid's control collection.)
//Then grab the last control in the DataTable's Collection
DataGridItem footer = 
    DataGrid1.Controls[0].Controls[DataGrid1.Controls[0].Controls.Count -1] as DataGridItem;
posted on January 27, 2006  #    by Mike Dugan  Comments [0]
 Friday, January 20, 2006

I love working with MS SQL Server. Oh sure, Oracle is supposedly faster and all that jazz, but when it comes to rapid development, MSSQL's ease of use just can't be beat. However, there are still some things to watch out for. Today I want to warn you all off of using UNION.

Conceptually, there's nothing wrong with UNION. It takes two result sets with congruent columns and returns them as a single result set. There are two potential performance issues, however, one of which can be quite serious.

The first problem is that the UNION operator automatically performs a DISTINCT operation on the combined result set. If it's unnecessary, or worse, if it is necessary, but you've also used SELECT DISTINCT in one or both of the UNIONed queries, then UNION is causing a loss of performance for nothing. The workaround to this is simple; use UNION ALL instead.

The second problem can be far worse. I've run into situations many times where the UNION (or UNION ALL) operator caused inexplicably poor performance. In one recent scenario, I used UNION ALL to merge the results of two queries, each of which ran in about 7 seconds. When merged using UNION ALL, query execution time leapt up to 1:20. Neither the estimated execution plan nor the actual one reflected this; the total subtree cost was exactly the same as the sum of the two queries' costs when run separately. The fix is a little more complex, but extremely effective.

My solution to the problem is to use a table variable. In general, I recommend the use of table variables over temp tables unless you absolutely need a feature that temp tables support but table variables do not.

Example

The example below is not intended to demonstrate good database design; it merely serves to illustrate the solution. Given the original query:

SELECT FirstName, LastName, Salary
FROM Employees
UNION ALL
SELECT FirstName, LastName, Salary
FROM Managers

First declare a table variable with columns that match that of the result set:

DECLARE @Result TABLE (
  FirstName varchar(50),
  LastName varchar(50),
  Salary money,
  -- Example of how to declare a PK within a table variable
  PRIMARY KEY ( LastName, FirstName )
)

Next, insert each query into the result table:

INSERT @Result
SELECT FirstName, LastName, Salary
FROM Employee

INSERT @Result
SELECT FirstName, LastName, Salary
FROM Manager

Finally, select the results:

SELECT *
FROM @Result

This workaround will not only avoid potential performance problems with UNION, but will actually allow you to improve join performance through the definition of a good primary key, if the UNION result is joined to other data.

posted on January 20, 2006  #    by Adam Anderson  Comments [1]
 Monday, January 16, 2006
DPack is a collection of free tools that extend the VS IDE. I originally got it because of the Delphi keyboard shortcut scheme, but I've come to appreciate for many of its other features, especially Surround With, which surrounds selected code with many different kinds of code constructs, and the Code Browser, which incrementally searches code for members matching a filter expression. Get it at http://www.usysware.com/dpack/
posted on January 16, 2006  #    by Adam Anderson  Comments [0]
 Friday, January 06, 2006

Add vertical guidelines to Visual Studio by adding a simple registry key to this location:

[HKEY_CURRENT_USER\Software\Microsoft\VisualStudio\7.1\Text Editor]

Now, add a string value named "Guides".

Set its value to something like RGB(128,0,0) 80, 100

The setting above will draw a Red guideline in Visual Studio at column 80 and 100. You can  have up to 13 guidelines. Adjust the values as you see fit.

Giving credit where credit is due, I believe Sara Ford first blogged about this about a year ago.

posted on January 6, 2006  #    by Mike Dugan  Comments [0]
 Friday, November 12, 2004

Build events are handy ways to perform tasks that need to be done every time a project is rebuilt. The Visual Studio IDE exposes these events through the project properties dialog for windows applications, but not for web applications. However, the IDE can be coerced into supporting build events for web applications, and it's surprisingly easy to do. Simply open your web project's .csproj file and look under the settings element for the following elements:

<VISUALSTUDIOPROJECT>
  <CSHARP>
    <BUILD>
      <SETTINGS>
        <!-- Your Build Events Here -->
        PreBuildEvent = ""
        PostBuildEvent = ""
        <!-- RunPostBuildEvent = "Always" | "OnBuildSuccess" | "OnOutputUpdated" -->
        RunPostBuildEvent = "OnBuildSuccess"
      </SETTINGS>
    </BUILD>
  </CSHARP>
</VISUALSTUDIOPROJECT>

Simply enter your command-line actions to PreBuildEvent and/or PostBuildEvent and enter the string describing the desired condition in RunPostBuildEvent. Save the file, and you're good to go.

posted on November 12, 2004  #    by Adam Anderson  Comments [0]

Delphi's DataModules were a useful programming tool because they provided a centralized place to define data schema and business logic which could then be referenced from multiple locations. Visual Studio .NET, however, encourages the programmer to place DataSets and Adapters on each individual page, resulting in decentralized and possibly redundant business logic. However, it is possible to roll your own DataModule-like components in .NET. Here's how:

  1. Decide whether you want to have your custom DataSet component in the same project or in a class library. Keeping it in the same project is a little simpler, while putting it in a separate class library will result in a separate assembly that contains only data logic, making it more suitable to deploy as a business tier.
  2. Add a new Component to the target project 
  3. Add a Connection and DataAdapters to the Component, and generate a DataSet. The visibility of the Connection and DataAdapters will default to private, so make sure to change them as appropriate for your design.
  4. Once the DataSet has been generated, create a new class that descends from the DataSet subclass. The DataSet subclass name is the same as the name of the .xsd schema file.
  5. Add the following attributes to your DataSet subclass:
    1. [ Serializable() ]
    2. [ System.ComponentModel.ToolboxItem( true ) ]
  6. Add a property to the DataSet subclass that references the Component containing all the DataAdapters. Include logic either in the DataSet subclass constructor or in the property accessor to create a new instance of the DataAdapter container if needed.
  7. Optionally, to hide the base DataSet class, open the base .cs file with the same name as the schema .xsd file and change the ToolboxItem attribute from true to false. Changes made to this file will disappear every time the .xsd it is based on changes, so make the decision whether or not this step is worthwhile accordingly.
  8. Build the project
  9. You can now add the custom DataSet class to the Toolbar. When dragged into another project, the Visual Studio IDE will treat the DataSet subclass as if it is a DataSet; it will appear as a top-level item in the DataBinding dialog box and have the DataSet property editor. Additionally, all the custom business logic methods you added to the class will also be available in code. The DataSet schema will only be editable in the project containing the DataSet subclass, which is a natural result of centralizing your schema and logic. Be forewarned that adding such a custom DataSet to the Toolbar while it is still in development might cause more frustration than it's worth, since Visual Studio will not be easily persuaded to detect changes to the DataSet schema, particularly in the designer.

Sample Code

using System;

namespace MyNamespace.MyDataModules
{

  [ Serializable() ]
  [ System.ComponentModel.ToolboxItem( true ) ]
  // MyStronglyTypedDataSet is the name of the DataSet class
  // automatically generated by Visual Studio .NET
  public class CustomDataSet : MyStronglyTypedDataSet
  {

    // CustomDataSetAdapters is the name of the Component class

    // containing all the DataAdapters used to generate, fill,

    // and update the DataSet.

    private CustomDataSetAdapters _Adapters;
    private System.ComponentModel.Container components = null;

    public CustomDataSet( System.ComponentModel.IContainer container )
    {
      container.Add( this );
      InitializeComponent();
      // Initialize DataSet properties here

_Adapters = new CustomDataSetAdapters( container );
    }

    public CustomDataSet()
    {
      // Initialize DataSet properties here

­_Adapters = new CustomDataSetAdapters();
    }
   
    protected override void Dispose( bool disposing )
    {
      if ( disposing )
      {
        if ( components != null )
        {
          components.Dispose();
        }
      }
      base.Dispose( disposing );
    }

 

    // This property exposes the component class containing

    // all the DataAdapters used to generate, fill, and update

    // the DataSet

    public CustomDataSetAdapters Adapters

    {

get

{

  return _Adapters;

}

    }

    public void BusinessLogicMethod1()
    {
      // Custom business logic here
    }
  }
}

posted on November 12, 2004  #    by Adam Anderson  Comments [0]

Expression columns are a useful feature of ADO.NET that allows developers to define calculated columns that are maintained on the client side using local data. This is a significant improvement over performing calculations within a SQL query, because expression columns can recalculate based on changes to local data without requiring a round trip to the database server, and less data needs to be transferred from server to client. Besides being able to perform simple arithmetic on values within the same row, expressions can also retrieve values from parent and child rows in a relational dataset, thus providing lookup and aggregation functionality as well. For detailed information on expression column syntax, see the DataColumn.Expression property help topic.

As useful as expression columns can potentially be, however, they come with a substantial limitation: they cannot be used in conjunction with an insert or update query that automatically refreshes row values from the database server. This is because of the way that the .NET framework applies the retrieved values during an update operation, which is to iterate through all columns in the table, expression columns included, and update their values, temporarily setting the ReadOnly property to false. When this operation is attempted on an expression column, the following exception message will result: “Cannot change ReadOnly property of the expression column.” This has been a known bug since 2002, and Microsoft has supposedly promised a fix for the next version of .NET, although it is unclear whether they mean the next version of the framework or of Visual Studio. Since the bug still exists in Visual Studio 2003 and .NET framework 1.1, one can only hope that this bug will be fixed in Visual Studio 2005 and .NET framework 2.0. Until then, there are several workarounds possible, which will be explained in the following paragraphs.

          Since this bug occurs when expression columns are mixed with insert or update queries that refresh the dataset, the only solution is to remove one of the two factors. The first and simplest solution is to remove the select query from the end of the insert or update query. However, this solution has a drawback in that the row will not be refreshed, which can be especially vexing if the database is generating the primary key field.

          The next solution takes the previous one a step further by writing custom code in the RowUpdated event. The RowUpdated event fires after changes have been applied to the database. Custom code could be added to this event to more selectively and intelligently update only the fields in the dataset that correspond to fields in the database. For example:

 

private void sqlDataAdapter1_RowUpdated(object sender,

  System.Data.SqlClient.SqlRowUpdatedEventArgs e)

{

  DataColumn dc = e.Row.Table.Columns[ "RowID" ];

  bool readOnly = dc.ReadOnly;

  dc.ReadOnly = false;

  try

  {

    SqlCommand cmd = new SqlCommand( "select @@IDENTITY", conn );

    e.Row[ "RowID" ] = cmd.ExecuteScalar();

    e.Row.AcceptChanges();

  }

  finally

  {

    dc.ReadOnly = readOnly;

  }

}

 

          Another possibility is to remove the expression columns from the bug-causing equation, rather than removing the automatically supported dataset refresh. In this approach, each call to DataAdapter.Update() would be bracketed by deletion and recreation of all expression fields in the DataSet. For example:

 

ArrayList al = new ArrayList();

 

// Save expression columns

foreach ( DataColumn dc in ds.Table1.Columns )

  if ( dc.Expression != "" )

    al.Add( dc );

 

// Delete saved columns from collection

foreach ( object o in al )

  ds.Table1.Columns.Remove( (DataColumn) o );

 

try

{

  da.Update( ds.Table1 );

}

finally

{

  // Restore saved columns

  foreach ( object o in al )

    ds.Table1.Columns.Add( (DataColumn) o );

}

 

          Lastly, some expression column functionality can be duplicated through custom data binding expressions. To do this, it is first necessary to explain the syntax data bindings created at design time. When creating a simple data binding through the Data Bindings dialog, Visual Studio creates a DataBinder.Eval() method call. The role of the DataBinder class is to simplify writing data binding expressions by eliminating two tasks required by data binding expressions that do not use DataBinder. These two tasks are: casting the object containing the data, and converting the data to the correct data type, which is typically a string. To see these advantages at work, consider the following line of code, which is a typical example of the kind of expression required to bind non-string data in a repeating control such as a DataList:

 

lblDisplay.Text =

  ((DataRowView) container.DataItem)[ "IntField" ].ToString();

 

If special formatting was needed, then the code would expand to the following:

 

lblDisplay.Text =

  String.Format("{0:d10}",

    ((DataRowView) container.DataItem)[ "IntField" ] );

 

This same task can be accomplished by using DataBinder.Eval(), as shown in the following example:

 

lblDisplay.Text =

  DataBinder.Eval( container.DataItem, "IntField", "{0:d10}" );

 

The benefit is that explicit casting isn’t necessary and the format string may be accessed as an extra parameter of the Eval() method rather than requiring a call to the static String.Format() method. However, the savings in typing have a cost in runtime speed. When writing your own custom data binding expressions, consider using the longer form as a way to improve application performance.

          What is important to understand when writing custom data binding expressions is that there isn’t anything magic about using DataBinder.Eval(). Any expression written in the page language that returns a value can be used as a custom data binding expression. Thus, to perform a simple lookup in a data binding expression, enter the following expression as a custom data binding expression. Assume the dataset ds contains two tables named Table and Lookup, with a relationship defined between them with the default name LookupTable. The example code returns the value of the field LookupValue in the table Lookup.

 

// Typed dataset syntax

ds.Table[0].GetLookupRow.LookupValue

 

// Untyped dataset syntax

ds.Tables["Table"].Rows[0].GetParentRow("LookupTable")["LookupValue"]

 

Likewise, aggregate expression columns can be replaced with equivalent DataTable.Compute() method calls. To extend the above example, assume that the table called Table has a child table called Child containing a field called Amount and related by the field TableID. The relationship in the typed dataset is called TableChild. The following expression column (defined in Table) and DataTable.Compute() calls will yield the same results:

 

// Expression column expression

Sum( Child( TableChild ).Amount )

 

// Equivalent DataTable.Compute() for the current TableRow

Child.Compute( "Sum( Amount )", "TableID=" + TableRow.TableID );

 

Armed with this knowledge, it may be possible to entirely avoid the need for creating any persistent expression columns in the dataset.

          Expression columns provide useful and powerful tools to create columns that can recalculate themselves in response to data changes without making a round trip to the database server while keeping the size of the data transmitted as compact as possible. However, in the current version of .NET, they come with significant limitations that will require extra effort on the developer’s part. If you need both the functionality of expression columns and to refresh row values from the database on insert or update, the approaches described above will provide you with several options with which to work around this bug. Here’s hoping it really gets fixed in the next version!

posted on November 12, 2004  #    by Adam Anderson  Comments [0]