Sunday, August 14, 2005

Codesmith: Code Generation v. Refactoring

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

Looking into CodeSmith, a good code generation tool, I started analyzing the differences between code generation and refactoring. Both help reduce redundancy. They are complimentary, with each concept having a different purpose:

  • Code Generation - Given input values and a template, automatically generate redundant code that varies only by the input values. This deals with how you create code.
  • Refactoring - Keep the functionality of your code the same, but improve the code itself via eliminating redundancy, improving clarity, etc... This deals with the final code, regardless of how that code was created.

Whenever you find yourself making repetitive code, you should at least check if these techniques can bail you out. Common examples may be (depending on your project) the data-access layer, the structure of business entities, documentation, unit tests, etc... Note that code generation and refactoring aren't opposites - but rather complementary. You can create a refactored class that still has some repetitiveness, and then code-generate it. For example, a fully refactored business entity may still have many properties that all follow the same format. For example, say your class has 10 such properties below. Given only the DataType and PropertyName, a code-generation template could crank this out:

public Double MyValue
{
    get
    {
        return _dblMyValue;
    }
    set
    {
        _dblMyValue = value;
    }
}

There are certainly places where repetitive code can be refactored such that there is no longer any redundancy, and therefore nothing to code-generate. A good code-generation tool is no excuse for not refactoring properly. However there are many places where refactoring alone is insufficient:

  1. Design time: Code generation is done at design-time, and thus offers a performance benefit over refactoring (executed at run time). Say you needed a class to access the properties of your business entities. You could use reflection at design time, or (assuming you know the entities) you could use code-generation to create the necessary code before hand.
  2. More functionality: Certain things can't effectively be refactored. If you needed to refactor a class that could handle many different Data Types, you'd need to use boxing./unboxing to handle the type-conversion. For example, pre-.Net 2.0 (which has generics) most collections, like HashTable or ArrayList, are not strongly typed. This requires boxing and unboxing which has obvious performance problems. .Net 1.1 does not provide a way to refactor the handling of various data types without the performance loss of boxing/unboxing. However you could use CodeSmith's strongly typed collection templates to automatically generate your own collections that don't require boxing. This is essentially still refactored because it provides additional functionality that the "refactored" version didn't meet - type safety.
  3. Beyond source code: A good code generation tool, like CodeSmith, can create any text file, not just object-oriented source code. For example, suppose you wanted to document your database schema. CodeSmith provides pre-packaged templates that do this. I am not a aware of a way that "refactoring" would solve this problem.
  4. Always automatic: Refactoring is great for object-oriented code with supporting unit tests. But it is difficult for many things outside of source code like documents or html pages. Short of other tools, these require a more manual approach. Code generation remains automatic

In conclusion, both refactoring and code generation are good things, but they are different and complimentary things.

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.