Archive for March, 2007

<compilation batch=”false”>

Today some of the C# / ASP.NET 2.0 code I created generated the following error “Circular file references are not allowed”.

After quite a bit of investigation, I realized that this was the result of how I had User Control’s organized within my website. For no good reason I was separating many of my user controls into sub directories within a parent “Controls” directory.

C:\….
+—Controls
+—– Admin
+—– Resources

By default, the compiler will batch assemblies (usually one per folder). This resulted in all files in the “Admin” folders being built into single assembly, while all files in the “Controls” folder where built into another.

In my situation I had a control in the root (Controls) referencing a control in “Admin”, while another control in “Admin” referenced one in “Controls”.

To temporarily get past the compiler issue one can set the batch attribute in the compilation element of the webconfig equal to false. This tells the ASP.NET compiler to not batch assemblies, and instead create an assembly for each user control. Now obviously this is not a good idea in production, but is a great way to get past the error in testing.

The long term solution is to remove the additional sub directories or better organize them.

Apollo

Check out this new product from Adobe Labs. It’s a cross-operating system runtime being developed that allows developers to leverage Flash, Flex, HTML, JavaScript, and Ajax to build and deploy rich Internet applications (RIAs) to the desktop.

http://labs.adobe.com/technologies/apollo/

URL Redirection with Mod-Rewrite ISAPI filter

Recently, one of our clients required that there entire site be optimized for search engines. As part of SEO our goal was to create hackable URL’s and avoid special characters on the query string. This meant no querystring arguments. We choose to use URL rewriting as a way to map dynamic virtual directories to database content.

Our goal was to take a friendly URL like below, and remap them to a more typical aspx page with arguments.

Input:
http://www.client.com/news/press/200703/aquisition
Output:
http://www.client.com/article.aspx?sectionid=22&issue=200703&name=acquisition

For the most part, URL rewriting in ASP.NET 2.0 is pretty straight forward. You create a class that implements System.Web.IHttpModule and register that class as a in the webconfig. All requests made to .aspx pages run through the handler prior to being redirected.

Unfortunately our goal for a friendly URL desired no file or extension, resulting in an interesting issue. Unless we placed a default.aspx page within each physical directory, the request would never fire through the ASP.NET process. Interestingly, ASP does not require this for the root of the site or once you hit 4 directories deep. In both of those situations, asp assumes default.aspx exists, resulting in the HtppModule firing.

Solution:

Our solution was to use a a free ISAPI filter called mod_rewrite (http://www.iismods.com/download.htm). This filter will allow us to append default.aspx to any requests that did not include a file with extension.

This filter is quite easy to use, it is completely configured via an INI file that uses regular expressions combined with back referencing to apply the rewrite. The following is the INI file we use to match requests with both a trailing slash “/” and without.

mod_rewrite.ini

Debug 1
Reload 2000
#Browse LOT
RewriteRule ^([^.]+)/([^/.]+)$ $1/$2/default.aspx
RewriteRule ^([^.]+)/$ $1/default.aspx

We have yet to test this under high load, but expect its performance to be adequate since it excludes everything but the page request.

Loading custom web control from an .aspx Code-Behind page

Yesterday one of our clients requested a simple change to a custom user control for one of their ASP.NET 2.0 site. We made the change, and deployed ONLY the .ascx file to their production server.

The deployment resulted in the following run-time error:

Unable to cast object of type ‘ASP.controls_customControl_ascx’ to type ‘controls_customControl’.

This error appeared to disappear by editing the webconfig file.

The structure of the control we edited was such that it dynamically loaded a second control in its code behind. This was done by making a reference to the custom control through the use of the @Register directive in the .ascx page.

<%@ Register src=”customControl.ascx” mce_src=”customControl.ascx” TagName=”CustomControl” TagPrefix=”uc1″ %>

In the code behind, the control was then loaded and explicitly cast to the appropriate type upon the click of a button in the control.

controls_customControl custom = (controls_customControl)LoadControl(”../controls/customControl.ascx”);
(NOTE: The dynamically loaded control inherits from a class defined in a precompiled assembly)

So what happen?

We all know that when the first request arrives for any page within a folder ASP.NET dynamically parses and compiles all the ASPX pages in that folder. We also know that it compiles any code-behind files associated with ASCX files into a separate assembly. If two pages exist in the same folder they will be compiled into the same assembly. Based on this understanding, I would have predicted that the file being changed would have been enough to trigger a recompile of the control folders assembly, eliminating the error.

Could the behavior come as a result of the @Register directive?

When you load a control programmatically the strong type for the control is available only after you have created a reference to it. By definition @Register is used for declarative inclusion of control in a page. The assembly referenced in the directive is dynamically created the first time the ASP.NET runtime accesses the resource. The instance of the control will be created “on-the-fly” when the page is loaded.

It has always been my understanding that when a programmer chooses to load a control in the code-behind, the strong type of the control is only available after the page has made reference to it, making @Register inappropriate. Without the implicit inclusion of the control in the page, the compiler should throw and error. I believe in our situation it worked because both controls exist in the same directory resulting in them being compiled into the same assembly.

Most documentation suggests that one should use the @Reference directive and NOT @Register when loading controls programmatically. @Reference tells .NET that the referenced control MUST be dynamically compiled and linked. If two controls in the same directory have dependencies on each other through an @Reference directive, they will end up in separate assemblies.

Would this have eliminated my error…time to do some testing!

….to be continued….

Introduction

My name is Joel, and I am a Microsoft.NET system architect and Director of Development for a leading interactive development firm located in Tempe, Arizona. Our firm designs and develops a wide range of custom web applications which leverage Flash, Ajax, ASP.NET and the Microsoft.Net Framework.