Monday, August 29, 2005

10 Tips on CodeSmith SubTemplates, Batch Mode, and XmlProperties

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

I've finished using CodeSmith to help with generating some of our tiers (or should I say "just started"?). So I posted some things I've learned from it: 10 Tips on SubTemplates, Batch Mode, and XmlProperties on the CodeSmith Peer Support Forum. This covers solutions I've found for:

  1. Passing values between subtemplates
  2. Handling that Sub template does not inherit parent's merge strategy
  3. Handling that target of RenderToFile must exist
  4. Handling Multi-level subtemplates
  5. Throwing and using errors in batch mode
  6. Embed XmlProperty filename instead of data
  7. Dynamically make your own batch file
  8. Only write new content
  9. Reusable code for XmlProperties
  10. Instantiate an XmlProperty at runtime

Details on the forum.

Wednesday, August 24, 2005

Using Sysinternal PsTools to find what has locked that process

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

Every get one of those frustrating errors "Cannot access file because it is being used by another process"? For example, you may be trying to delete or rename a file, but this error message won't let you. Sometimes it is as simple as closing an application that obviously has locked that file, but sometimes it's just more complicated - perhaps some service (which has no GUI, and therefore may be less intuitive) has locked the file. Ideally you could just click the file in Windows Explorer and it would tell you what locked the file and what to do.

There is a solution - Sysinternals provides a free tool, Process Explorer, that lets you find all the processes accessing a file. The tools requires no MSI; you can directly copy it to your machine and run it. It's like Task Manager on steroids (in a good sense).

Initially one may make sure that they don't have the file open in some sort of editor, but the problem may persist even if everything is closed. Go to Find > Find Handle, and type in the name or directory of the file. Click search, and it will show you all processes accessing that file. You can now kill those processes such that you can delete the file.

This is actually very educational - you'll see that IIS, other instances of Windows Explorer, certain background services, or apps that you wouldn't think of, all can lock files.

Monday, August 22, 2005

CodeSmith: Beyond Just Generation - Doing Simple Data Analysis

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

I've made several CodeSmith related posts on this blog:

There's another topic I'd like to explore - using CodeSmith for analyzing  Xml data and Database schemas. For example, suppose you wanted to compare if two tables had similar schemas. For example I wanted to see if one database had different column lengths than another for all tables in the database. There's several ways you can do this:

  • Manual --> bad (too slow, error prone, tedious, not-reproducible)
  • Build your own custom console app, use ADO.Net --> very slow to create, to easy to have errors
  • Find a custom tool specifically for DB comparisons --> yet another tool to learn, may provide far more functionality than needed
  • Use CodeSmith's Schema Explorer --> has potential!

CodeSmith's Schema explorer is extensive - it lets you start at the database and drill down to its collections of tables, columns, and individual properties. So it has the info needed for most tasks. It's also easy to use, so it saves you time in writing your own equivalent object. It's also a reusable tool. I.e. if you need to learn a technology, learn one that is helps out with more than just the current problem.  You could create two SchemaExplorer.DatabaseSchema objects, and then cycle through their tables and columns, comparing them.

CodeSmith also has the XmlProperty, which lets you analyze an Xml document. You could program compare the Xml doc to the database schema.

The "generated code" could be the results of your analysis. For example, wherever the condition you're searching for is met, you could Response.WriteLine the necessary info.

While CodeSmith is designed for CodeGeneration, it also can be used for simple analysis of data sources that are commonly used as inputs for that generate - such as Xml docs and Database schemas.

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.