Thursday, August 11, 2005

Things that fundamentally change how you program

[This was originally posted at http://timstall.dotnetdevelopersjournal.com/things_that_fundamentally_change_how_you_program.htm]

There are a lot of things that fundamentally change how you program - you see something and the light bulb goes off. For me, the following was such a checklist (not in any order)
  • Object Oriented Code --> Many VB6 developers haven't needed to deal with OOP yet. However VB.Net is now object-oriented.
  • Unit Tests --> automated tests for specific units of code to assist with development and regression (usually run with NUnit)
  • Regex Expressions --> Manipulating text
  • Xml --> Consistent and standard data storage
  • Code Generation --> eliminate redundant code that can't just be refactored.
  • Consciously seeking Tools --> tools help you avoid or simplify tedious tasks.

Many people do some of these, but I've seen many developers reluctant to invest in the other areas. They'll write lines of text-parsing code before spending two hours investing in Regular Expressions.

I'm currently looking more into code generation with CodeSmith. It is a great tool, and I plan to post some cool findings on it.

Sunday, August 7, 2005

Referencing lists of Html controls in Repeaters and DataGrids

[This was originally posted at http://timstall.dotnetdevelopersjournal.com/referencing_lists_of_html_controls_in_repeaters_and_datagrid.htm]

If you have list that you bind to a repeater, and you need to dynamically create html objects for each row (such as adding a checkbox or hidden field for each row). .Net provides ways to cleanly handle those Html controls using the "name" attribute.

Note that each html control can have an ID (which must be unique) and a Name (which need not be unique). If you assign your controls a common name attribute, such as giving each checkbox in the repeater rows the name "chkA", then you can easily access those fields. Say you had the following repeater:


           
           
       
   


   
       

... other columns

On the server, you can create an array of the selected values like:

string[] s = Page.Request["ChkA"].Split(',');

On the client, you can access the fields via the document.getElementsByName('name'):

function DoClick() {
    var v = document.getElementsByName("ChkA");
    var s = "";
    for (var i = 0; i < v.length; i++) {
        if (v[i].checked) {
            s = s + v[i].value + ",";
        }
    }
}

Wednesday, August 3, 2005

More Visual Studio.Net IDE errors - Part III

[This was originally posted at http://timstall.dotnetdevelopersjournal.com/more_visual_studionet_ide_errors__part_iii.htm]

I had mentioned before that I was having problems directly running web applications, so I resorted to manually attaching to the process. This worked fine for server code, but it was insufficient to debug client JavaScript. So, I needed to buckle down and fix it.

I kept getting the error: "Error while trying to run project: Unable to start debugging on the web server. The COM+ registry database detected a system error". I wasn't finding a solution with the associated help file.

Finally I installed the .Net 1.1 Service Pack 1, and that fixed it.

I also heard that running %WINDOWS%\system32\secedit.exe would fix the registry problem, but I have not personally tried it.

More Visual Studio.Net IDE errors - solved

[This was originally posted at http://timstall.dotnetdevelopersjournal.com/more_visual_studionet_ide_errors__solved.htm]

After my harddrive crashed last week and I needed everything reinstalled, I've been running into errors with Visual Studio's IDE. A new one that I encountered was the inability to add classes or WebForms. I would try adding a WebForm, and get an error like:

Cannot find: C:\Program Files\Microsoft Visual Studio .NET 2003\VC#\VC#Wizards\CSharpAddWebFormWiz\Templates\1033\NewComponent.cs

There are just some things that you take for granted, so this was a little frustrating. I checked to see this file location, and I saw a structure like:

  1. [Folder] Microsoft Visual Studio .NET 2003
    1. [Folder] VC#
      1. [Folder] VC#Wizards
        1. [Folder] CSharpAddWebFormWiz
          1. [Folder] Scripts
            1. [Folder] 1033
              1. default.js
          2. [Folder] Templates
            1. [Folder] 1033
              1. Templates.inf
              2. WebForm1.aspx

I saw two existing files: Template.inf and WebForm1.aspx - no "NewComponent.cs" file. At first I though perhaps the file was missing, then I did a search for "NewComponent.cs" and saw it was the sole contents of the Template.inf file. I reasoned that Template.inf contained the name of the template to call. So I changed Template.inf from that to "WebForm1.aspx", and it worked perfectly. I verified that other machines, were this wasn't a problem, has the same contents for their template file too.

While I'm glad it was a simple fix, It seems like such a strange thing to happen.

On a related note, this article by Deborah Kurata shows how to make your own Code Templates that you can add in Visual Studio, just like the wizards for adding classes or WebForms.

Monday, August 1, 2005

Solving some Visual Studio IDE Errors

[This was originally posted at http://timstall.dotnetdevelopersjournal.com/solving_some_visual_studio_ide_errors.htm]

My harddrive crashed the other day, which caused me to re-install our entire Visual Studio.Net solution. Initially all seemed well, but then I stumbled across three environmental problems:

  • I couldn't open certain web pages in design mode (that inherited directly from a non-System.Web.UI.Page class
  • I couldn't navigate to definitions for methods, properties, variables, etc...
  • I couldn't directly debug a web application.

While I could still technically write code, the lack of these features is just annoying. I solved the first two by deleting the \bin and \obj directories and rebuilding. I'm guessing that some meta data got corrupted and this resetted it. I actually created a batch file that just takes care of this for me now; something as simple as:

rmdir /Q /S C:\MyProject\Web\bin
rmdir /Q /S C:\MyProject\Web\obj

The third issue I still wasn't able to directly resolve. However I can get around it by manually attaching to the process. To manually attach:

  1. Click Debug > Start without debugging
  2. Click Debug > Processes
  3. Select the aspnet worker process and click "attach".

I actually like this more because it's much quicker to start. I guess that makes the hard-drive crash a blessing in disguise.

Thursday, July 28, 2005

CSS Tip: Using media=print for printer-friendly pages

[This was originally posted at http://timstall.dotnetdevelopersjournal.com/css_tip_using_mediaprint_for_printerfriendly_pages.htm]

When you print a page, you want just the workspace of the page in normal portrait view - not banner ads, left navigation bars or text that extends off the printed page . This is a printer-friendly view. There are at least two categories of solutions:

  1. Have a "click here for printer-friendly version" link that posts back to the server, does manipulation such as hiding menus, and creates a new instance of the page in printer-friendly mode
  2. Use Cascading Style Sheets (CSS).

The former has several problems that the latter solves:

  • It posts back to the server, which hurts performance
  • It becomes a nightmare if you want to open a new instance of the page for printing, such that closing this print-page won't accidentally close the main app page.
  • It is a pain for editable pages because it requires the developer to manually persist all values to the new print page. Say the user enters a value in a text field, how do you persist that to a new page instance, perhaps via querystring or session? But what if the values are large?
  • It requires additional effort for each page.

CSS provides a "media" attribute that can apply different style sheets to different media. The two media we're looking at here are screen (default) and print.

A stylesheet can set the color, visibility, or any other style that's relevant to a print-version. Here's a practical example that hides buttons (you can click a button in printview) and formats all text in greycolor (you many not want to waste color printing on text):

Html page:

<HTML>
    <HEAD>
       
        <LINK href="Print.css" type="text/css" rel="stylesheet" media="print">
        <LINK href="StyleSheet1.css" type="text/css" rel="stylesheet" media="screen">
    HEAD>
    <body MS_POSITIONING="FlowLayout">
        <form id="Form1" method="post" runat="server">
            <P>This tests styles.P>
            <P>
                <asp:Label id="Label1" runat="server" CssClass="MainFont">
                Labelasp:Label>P>
            <P>
                <asp:Button id="Button1" runat="server" CssClass="PrintButton"
                Text="Button">asp:Button>P>
        form>
    body>
HTML
>

Stylesheet (Normal):

body
{
}
.MainFont
{
    font-weight: bold;
    color: red;
}

Stylesheet (Print):

body
{
}
.PrintButton
{
    display: none;
}
.Template
{
    display: none;
}

Eric Myer has a great article that explains how to use CSS for printing.

Monday, July 25, 2005

Tips to Efficiently create database unit tests

[This was originally posted at http://timstall.dotnetdevelopersjournal.com/tips_to_efficiently_create_database_unit_tests.htm]

A while ago I published an article and blogged on how to Unit Test the Data Layer. Recall that the basic approach is:

  1. Create a test-only instance of the database
  2. Initialize the table data to a specific state
  3. Run your object
  4. Check either the return value of that object (for selects) or the new state of the database (for insert/update/delete).

A common problem I keep facing is that these tests can take long to create, and longer to modify. Below are a few tips that I've been learning.

Creating the initialization script is usually tedious and time-consuming. One approach is to create or find a tool that takes a select statement and generates inserts from its result set (I built such a tool, but I can't release it because it's proprietary to my company, Ameripay). This lets you easily copy existing data for testing purposes.

I modified this tool to put tabs after each column, and place the insert and values sections on separate lines.

insert into myTable ( myId,    col1,     col2)
values ( '1',     'abc',     'def')

This lets me easily copy the text into Excel, which then automatically formats it in a tabular way that lines up column headers with contents, like so:

insert into myTable(myId,col1,col2)
values('1','abc','def')

It's much easier to edit this way!

Therefore to maintain the data, I only need the raw SQL script itself - not some fancy generator/mapping tool or spreadsheet. I can now take the raw text and swap it between:

  • Query Analyzer -  to easily test it
  • Visual Studio - in a source file as the contents of an ExecuteNonQuery(@"...") method that runs it to initialize the test
  • Excel - to put it in tabular form and maintain it.

Two applications of this:

  1. I created a MasterInserts Excel sheet that contains standard inserts for each table. Therefore if I have 5 objects all requiring test data from the same table, I go to this master sheet as the definitive source, copy the lines I need, and modify them appropriately. This prevents me from wasting time re-creating SQL inserts for tables.
  2. Say I have multiple tests for a single object. 90% of the base data for each test is the same - usually it's just tweaking a value like making sure a SP handles a null column. Therefore I refactor the common base script to a single method that runs it in ExecuteNonQuery. Then each test can call this method and merely do the simple update or additional insert that it needs.

The sky is always the limit, but it's getting there.