Thursday, July 14, 2005

Dynamic Controls Everywhere - Part Two: Control Creation

A couple of months ago I implemented a simple control builder in a test application to generate dynamic controls using the System.Activator of the .NET Framerwork. The class added the created control to a ControlCollection (what a coincidence). It worked very well, but it suffered from two flaws- 1.) I had to maintain a disgusting switch statement for any type of control that I wanted to render, and 2.) I had to recompile when I needed additional control support.

In order to avoid the switch statement and the recompile, I needed to perform the same operations, but use a cached configuration file to provide the controls required for render instead of the switch statement. I managed to implement this using Reflection instead of using the System.Activator - naturally I ran into some problems that lead me to this solution.

The scenario...

In order to use the Activator's "CreateInstance" method you need to specify the object's Type (the easiest overloaded memeber IMO). I recently learned that the GetType( string typeName ) method of the System.Type namespace only works on initialized objects (would defeat the purpose of creating dynamic controls) and Types in the current working assembly (not sure if this is the right term). For instance, if you are in namespace MyProject.Web and have a type MyProject.Web.MyCustomPage the GetType( "MyProject.Web.MyCustomPage" ) will return a type object. However, if you need to perform GetType on MyOtherProject.WebControls.SomeCustomControl, you're out of luck because GetType only looks in the current assembly.

Okay, so how can I get the right assembly loaded so that the GetType looks in the correct assembly for the correct type name so that I can initialize my control? I started poking around the MSDN documentation looking into Reflection and I found out that the System.Reflection.Assembly object has the very same CreateInstance method for returning a type. Except that it requires an instance of the Assembly. Easy enough, right? Well, yes and no!

It's easy to create an Object in most OO languages, however, in order to create an instance of the assembly you need the fully qualified assembly name. So I built a quick console application that would write my assembly's fully qualified name (not the easiest, but it was quick and dirty) to a text file and I just copied that into the necessary constructor and voila. Assembly instance and now the ability to create an instance of any public, non-sealed member of that Assembly.

All that is left to do is build the config file for the dynamic controls and all will be right in the World. Stay tuned!

Thursday, July 07, 2005

Dynamic Controls Everywhere - Part One: Problem/Initial Solution

One of the tasks I have been given requires me to add a “portal” (for lack of a better term) to our existing intranet website for reporting purposes. The current report section suffers from many problems, the least of which is that it requires a complete application recompile to add a simple link to a new report for our users – terribly inconvenient. The new report section had to be, above all else, configurable (read config files – lots of config files).

A “report” in our application consists of a set of search criteria – e.g. show me sales for business unit A for Q1 of this year (or any year for that matter); the corresponding parameter names required for the report; and a hyper link to a separate web server that generates the report. Most of the aforementioned requirements are hard-coded into the code-behind of the ASP.NET page which makes it difficult to maintain. The current interface lists every possible criterion, as some sort of HTML input control, regardless of the report’s requirement. Once the search options are put into the form fields, the user has to post the page back – we like the client, so let’s try to fix this – which then re-builds all of the report links. Then the user can click on any link and open the corresponding Crystal Report.

Yuck! That’s all I can say. Hard-coding business logic into code-behinds is a big no-no if you ask any software developer. Not only do you have to manage the business logic, but any time a change is needed we have to recompile and redeploy the entire application. This means booting all user sessions and forcing the server to recompile the app into MIL. To resolve part of this problem, I created a configuration file with the following XML arrays that then get loaded into a cached (cache = performance boost when memory is available) custom class.

<criterion>
<criteria type="”“" description="”“" label="”“" assemblyname="”“">parameterName=”“ />
</criterion>
<reports>
<navigationitem description="”“" label="”“" hyperlink="”“">
</reports>

When the Report page is called, the page loads the configuration file (either from the cache or deserializes it using the XmlSerializer) and supplies the custom collections to the Report through Properties – Reports and Criterion (what a coincidence). The page then loops through each collection building the criteria and report links sections of the page with the corresponding collection. Any time we need to add a criteria item or report – we just modify the configuration file and the necessary items just “show up” on the page. Look Ma, no recompilation (twice if you’re keeping count), no redeployment, and we have achieved the maintenance ease.

E-mail me if you would like to see the source code and configuration files.

-Brian

Tuesday, July 05, 2005

Entrance Into the Blogosphere

I have finally decided to enter the world of blogging to share my thoughts on .NET, programming, other blogs and anything else that suits my fancy. My first "real" post will be up in a few days when I have a working sample. The post will involve dynamic web controls, .NET reflection, and a config file.

Stay tuned (or not :})

-Brian