Publishing ASP.Net Applications from the Command Line

April 30, 2008

Visual Studio 2005 has a built-in Publish command which generates a set of (more or less) randomly named DLLs, instead of a single DLL that was generated back in the good old days of ASP.Net 1.1.

An add-on, the VS 2005 Web Deployment Projects, offers the functionality to generate a single assembly for the web application, among other useful things, and includes the aspnet_merge tool.

The publishing of an application usually always takes the same steps with the same options, and can take a couple of minutes (depending on your project). Since repetitive tasks are a source for errors, I wanted to automate this procedure.

I wanted to take a look under the hood and create a batch file which compiles a web application from source files using aspnet_compiler and then calls aspnet_merge.

The first problem I met was that aspnet_merge was not in the PATH. Rather it is being installed into

C:\Program Files\MSBuild\Microsoft\WebDeployment\v8.0

which is a localized path, so you either change the PATH variable, or copy aspnet_merge into a directory in the PATH.

I created a batch file called publish.cmd, which looks like this:

if "%1"=="" goto errnoparam
if not exist c:\inetpub\wwwroot\%1\nul goto errnotfound

setlocal
call "C:\Program Files\Microsoft Visual Studio 8\VC\vcvarsall.bat" x86
echo on 

set base=c:\archive\publish\%1
if not exist %base%\nul md %base%
rd /s /q %base%

echo compiling to %base%
aspnet_compiler -v /%1 -f -d %base%
aspnet_merge %base% -w %1 -debug 

endlocal
echo web compiled to %base%
goto end

:errnoparam
echo usage: %0 web
goto end

:errnotfound
echo web %1 not found
goto end

:end

First, the script checks whether the name for the web application is provided, and whether it is a valid existing name. Next, the VS2005 environment variables are initialized.

Finally it calculates the name of the output directory, and invokes aspnet_compiler and aspnet_merge.

The result:

  • beautifully named DLL files
  • empty .aspx and .ascx files (so nobody steals your markup ;) )
  • ugly .compiled files in the bin directory (cannot be deleted without breaking the application)

Online Help for Web Applications

April 28, 2008

I developed a system to provide online help for users of an ASP.Net application. The content of the documentation is stored in a Mediawiki wiki.

Creating the help system consists of 4 steps:

  1. Setting up the wiki
  2. Linking the web application to the wiki
  3. Creating content stubs for each ASP.Net page
  4. Editing the content

From a developer’s point of view, steps 2 and 3 are the most interesting.

Setting up the wiki

As I chose to have the online help in a separate namespace, it has to be registered in the LocalSettings.php:

# > 100, even = content namespace
$wgExtraNamespaces[100] = "OnlineHelp";
$wgContentNamespaces[] = 100;

Linking to the wiki

My ASP.Net 1.1 applications were frame-based, but as version 2.0 introduced Masterpages, developers are encouraged to use them.

The advantage of Masterpages is that they share layout and functionality with the embedded aspx pages, and every page has a unique URL (as opposed to a frame-based solution).

Therefore the simplest way is to add a “Help” HyperLink to the masterpage, and set the link’s NavigateUrl to point to the wiki entry in the Page_Load event:

    protected void SetupHelp(HyperLink hlHelp)
    {
        if (!IsPostBack && !Page.IsAsync)
        {
            string s = Request.
               AppRelativeCurrentExecutionFilePath.Substring(2);
            hlHelp.NavigateUrl = GetWikiUrl() +
               s.Split('.')[0].Replace('_', '-');
            hlHelp.Visible = true;
        }
    }

This procedure retrieves the name of the current page (cutting off the leading “~/” and trailing “.aspx” plus query parameters), and adds the wiki’s base url. (I also chose to replace underscores by dashes, too).

Creating content stubs

To create wiki content, I developed a wikibot which posts pages into the wiki.

A command shell script iterates through all .aspx pages, and posts the contents of a pre-defined text file into the wiki, using the page’s name as the title (as required by the C# code above).

Additionally the script also generates an index page which links to all page entries in the wiki.


Analyze User Control usage and Hyperlinks

April 25, 2008

This series describes how to analyze ASP.Net source code.

Which page links where

To find hard-coded links in your ASP.Net pages, retrieve the NavigateUrl and DataNavigateUrlFormat properties, like this:

graspx -col 1,5 l NavigateUrl
graspx -col 1,5 l DataNavigateUrlFormatString

Which User Controls are used in my application

To list which page registered which user control, run:

graspx -col 1,5 l Src

If you omit the page name in the output, you can count how many pages reference a given User Control:

graspx -col 5 -count l Src

Which assemblies do your pages rely on?

graspx -col 5 l Assembly

Which Session Fields does your application reference?

graspx -col 5 -count l SessionField

graspx is available for download here.


Analyze ASP.Net Source Code with graspx

April 24, 2008

This series describes how to analyze ASP.Net source code. Follow the graspx category for more articles.

QueryString Parameters

To find out which query string parameters your pages are expecting, run:

graspx -col 1,5 l QueryStringField

Database Statements

To retrieve all SQL statements your application is issuing, use the following commands:

graspx -col 1,5 l SelectCommand
graspx -col 1,5 l InsertCommand
graspx -col 1,5 l UpdateCommand
graspx -col 1,5 l DeleteCommand

This also lists the aspx file name. If you want the SQL statements only, set the -col parameter to 5.

The output of these commands can be further analyzed to check whether the referenced tables and stored procedures are still in use.

graspx is available for download here.


FormView: no Update inside Run-at-Server Table control

April 24, 2008

I am not sure whether this behavior is documented somewhere, so I simply tell you from my recent programming adventure ;)

In a FormView, I usually arrange the controls inside EditItemTemplate in a table. In this particular case, only selected fields should be edited, depending on some kind of status information.

Since each field (plus prompt) occupies a single row in the table, my choice was straightforward to declare the dynamic table rows as

<tr runat="server" id="trSomething">

However, the form behaved strangely, namely it did not retrieve the control values bound with Bind() into the UpdateCommand’s parameter collection.

To debug, I added Updated and Updating events to the SqlDataSource, and a ItemUpdating event to the FormView, where the NewValues collection of FormViewUpdateEventArgs always gave me an empty dictionary.

I dug further into the generated code somewhere below the Temporary ASP.Net Files directory, and found that no code was generated to retrieve the control values into a dictionary, which should happen during a Bind in Update.

Then I realized that somehow a control bound for update must never be inside any other control marked as Run As Server Control. And indeed, removing the runat tag resolved the mystery.

So if you have controls inside a FormView which are to be displayed or hidden depending the state of a record, you have to hide or display them one by one, as rearranging tables dynamically will break your database functionality.


Checking ASP.Net Source Code with graspx, part 2

April 21, 2008

In yesterday’s post about graspx, I demonstrated some usages of the tool that I originally had not thought of, and it soon turned out that Windows does not provide the necessary functionality (namely a unique option in the sort command).

Therefore I added two new switches to graspx:

-col selects which columns to appear in the output (implicitly sorting the generated output and filtering unique lines

-count adds a new column to indicate the number of occurrences of each line of the result

The following examples illustrate the simplifications of yesterday’s examples.

Check EmptyDataText

To retrieve the filename and the value of the EmptyDataText attribute, run:

graspx -col 1,5 l EmptyDataText

Check CSS references

To find all CSS classes referenced in aspx pages, run:

graspx -col 5 -count l CssClass
graspx -col 5 -count l class

With the new functionality it is much easier to retrieve relevant information from your source code.

graspx is available for download here.


Checking ASP.Net Source Code with graspx

April 20, 2008

In a recent post, I introduced graspx, a tool to check aspx source code files.

Let me show you some usages of this tool (usage requires that you cd to your web development directory):

Check XML compliance

Find all aspx files that are not XML-compliant:

graspx c -ce

Display all ascx files that are not XML-compliant

graspx c -ce *.ascx

Check page titles

To find all aspx files with no set page title, run

graspx l Title | find "Untitled Page"

Check EmptyDataText

If you copy and paste practice code reuse often, you may sometimes overlook changes in texts in the GUI which are not always displayed, such as GridView.EmptyDataText.

To retrieve list of all messages in EmptyDataText, run

graspx l EmptyDataText

To reduce the output to relevant information, namely filename plus text, add the for command (type “help for” for explanation):

for /f "usebackq delims=: tokens=1,5" %i in
  (`graspx l EmptyDataText`) do @echo %i: %j

Check CSS references

These procedures are useful if you want to clean up your CSS definitions:

Find all elements with inline styles:

graspx l style

Find all CSS styles referenced by your ASP.Net files:

graspx l class
graspx l CssClass
graspx l class *.master
graspx | CssClass *.master
graspx l class *.ascx
graspx | CssClass *.ascx

Get sorted results:

(for /f "usebackq delims=: tokens=5" %i in
  (`graspx l CssClass`) do @echo %i) | sort

Unfortunately, Windows sort lacks functionality to return unique results.

To be continued…


Management Studio + Generate Script = Funny Error

April 18, 2008

I just tried to script a database that I am working on, when Management Studio gave me this fine error message:

- Determining objects in database 'mydatabase' that will be scripted. (Error)
Messages
An item with the same key has already been added. (System.Windows.Forms)

The Progress window lists all database objects as Stopped, and it is impossible to figure out exactly which object causes to error message.

Checking the database for duplicate names (unlikely, but just to be sure)

select name, count(object_id) from sys.objects
group by name
order by count(object_id) desc

did not show any name occurring more than once, of course.

The most annoying point is, that the error is not raised because of technical problems (meaning, invalid information in the database (hopefully!)), but by an exception raised by System.Windows.Forms because a ListBoxItem (or whatever) violated some fancy UI constraint that has nothing to do with the task to be performed.

The internet knows nothing about this.

Update: Here’s the solution

Read the rest of this entry »


Introducing graspx

April 16, 2008

Every web developer needs to search source code every now and then. Tools like find, grep, or an IDE’s “Find” function can only search full text, without regard to context.

Since ASP.Net files (aspx, ascx) are XML-based, and should therefore be XML-compliant, an XML-aware tool searches more efficiently.

I have previously written about parsing XML/XHTML files with C# here and here.

Enter graspx (grep for aspx), a command-line tool to check and search any XHTML/XML-based files created in Visual Studio 2005.

As a first step, use graspx to check whether your files are XML-compliant. Unfortunately, files created with Visual Studio are not always strict XML, although VS2005 tends to be more compliant than VS2003.

   graspx c *.aspx
graspx c *.ascx
graspx c *.masterpage

graspx will report if files are not XML-compliant (missing closing tags, missing quotes for attribute values).

As all files are verified, you can

  • search for text occurrences in attribute values
  • list the values of a specific attribute

Invoking graspx without parameters will display the help text with all available commands and switches.

graspx can be downloaded here.


Replicate String in C#

April 11, 2008

I was surprised to find that the string class in C# does not have a Replicate() method.

The fastest way (in terms of lines of code) to replicate a given string seems to be a combination of string.Concat() and ArrayList.Repeat()

string.Concat(System.Collections.ArrayList.Repeat(SourceString,
NumberOfRepetitions).ToArray());

For example, if you want to indicate that a password has been provided, but should not be displayed (obviously!), use the line

LabelPassword.Text = string.Concat(
System.Collections.ArrayList.Repeat('*',
LabelPassword.Text.Length).ToArray());

in the DataBinding event of the password label control.