WebForms and alternatives: You can’t convince everyone

Since starting with MonoRail some months ago, I’ve joined the bandwagon touting its benefits over Web Forms. Part of my advocacy was to convince the developers who help me out with my online baseball game, CSFBL (which will be rewritten using MonoRail).

In a discussion on our BaseCamp site, one of those developers, Rick, said this. (Emphasis added by me.)

I finally got to spend some time with MonoRail on Friday at work… [T]he guy that sits next to me does dev work on a fairly complex ASP site… He’s just got tasked to add a whole new section of his site and he doesn’t want to do it in ASP and we both got tasked to start learning each others projects so that we can back each other up when one of us is out so we decided to give MonoRail a look. He had spent all week just trying to get up to speed on ASP.Net and he was getting very frustrated… we spent about an hour going through the first tutorial before we both got interrupted to do other things, but we liked what we saw so far. We plan on picking up right where we left off first thing Monday morning.

I read your latest blog post about MonoRail vs ASP.Net and I’m one of those coming from the VB background that found Web Forms easy to learn. But after looking at all your links and the demos I can’t wait to learn this stuff. You nailed it on the head at the end when you said you’d rather learn this than fight with the GridView… that’s the part of Web Forms that I hate. I can’t count the hours I’ve wasted on the old DataGrid in ASP.Net 1.1 and my coworker spent all week fighting with the Wizard and GridView controls.

Cool stuff. 🙂

Cool stuff, indeed. Unfortunately, it’s not so easy to convince everyone. Here’s a follow-up reply he made. Again, emphasis added is mine.

Not so fast…I chatted with my co-worker this morning and he made some great progress with the GridView over the weekend and was very excited about what he could do with it. He said he’s not very excited about doing more Javascript and HTML coding and that he’d rather switch over to Web Forms. DOH! So… tomorrow we’re going to check out the Web Client Factory Software.

My reply went something like this.

Are you a web developer or not? If you are, you should know JavaScript and HTML. Otherwise, you are not a web developer, you are a Web Forms developer. There’s a big difference. There’s nothing wrong with being either; you just have to understand the constraints of both.

As for doing more JavaScript as opposed to doing more Web Forms… Just wait until you start writing OnItemDataBound event handlers for your GridView to do some custom table rendering, as opposed to simple injection into your presentation layer (i.e. web page). You’ll realize that staying with Web Forms will just mean you write more C# code to handle your presentation instead of presentation code (i.e. HTML and JavaScript and whatever is used to inject data into your presentation layer).

The good thing about all of this is we have options: Web Forms, MonoRail, and many others. What’s better for one person (or one project) is not necessarily better for everyone (or all projects). People should try different solutions, and ultimately choose the one based on what are you most comfortable with and what makes you a better, more efficient developer.

Introducing WilsonORWrapper: A wrapper for Paul Wilson’s O/R Mapper

Earlier this month, I wrote about a revamp to the O/R mapper templates I’ve been working on, and included a demo project that included a working sample. Since then I’ve continued some work on these, have cleaned up the project, and added it to a public Subversion repository. Rather than write a lengthy introduction, I’ll just cut-and-paste (and reformat) the text from the readme file in the repository.

Wilson O/R Wrapper

What is it?

WilsonORWrapper is a framework that wraps around Paul Wilson’s O/R Mapper (“WORM”) that creates entity objects and a service layer for your O/R mapper objects. It is implemented as a set of assemblies that provide base functionality and a set of CodeSmith templates that can autogenerate your entity and service layers.

Who wrote it?

The author of the WilsonORWrapper project is Brian DeMarzo. However, a large part of the code contained within it is derived from CodeSmith templates by Paul Welter. Other smaller code blocks are derived from general tips pulled from various web sources; credits to the authors where relevant are given within the source code.

How to use it

To use the wrapper, you first need to do three things: create your database, create your mappings file, and create your projects.

CodeSmith templates are provided to build your mappings file, which you should always review and edit manually after creation.

CodeSmith templates are provided to build your entity and service classes. These templates will also create or update a Visual Studio project for both your entity and service layers, and will optionally create a test project (NUnit required).

Further information can be found at the WilsonORWrapper web site. Source code (which includes an example project) can be downloaded from the Subverson repository or from the project web site.

Yet another comparison of MonoRail vs WebForms

It seems everyone is writing about the differences between MonoRail and WebForms these days. Not to be left out, I’ll throw a few of my own comments into the mix.

I think that one’s attraction (or hatred) of MonoRail or ASP.Net’s WebForms is based on their programming background. (At least, it is in my case, and that’s at least one person, so that statement can’t be totally wrong.)

  • People who come from the old days of web programming (before ASP.Net), who are used to coding HTML and JavaScript by hand, or who are used and comfortable with injecting presentation code into web pages will feel comfortable with MonoRail.
  • People who come from the old days of Visual Basic programming or whose experience with web programming is WebForms and ASP.Net will feel comfortable with ASP.Net.

For all its flaws, WebForms has a lot of power, but the power comes at a price. Web forms does a lot for you, which is its benefit and its bane. If you want total control of your application, you’ll be frustrated with WebForms. MonoRail leaves a lot of things in your hands (particularly on the presentation side), and tries to help you do things rather than do things for you. That’s a big difference.

One of the reasons for the popularity of ASP.Net and WebForms (aside from Microsoft, of course) are the many web controls that come built-in or that you can buy to extend your application’s capabilities without having to write your own complex code. The same issues described above, however, still apply. Whenever you rely on a web control to do a complex task for you, you’re sacrificing some control.

That’s not to say there are no "web-control-like" solutions for MonoRail. There are many community-developed scripts and code blocks that can be used to extend the functionality of a web site built on MonoRail, providing features similar to web controls with less mysterious baggage. Consider the following.

Continue reading

A quick introduction to NAnt

There’s lots of open source libraries out there which have become ubiquitous in today’s world. One of those is NAnt, a .Net build tool.

I’ve wanted to use NAnt for a while but never had a sufficient pressing need to do so. After forcing myself to get familiar with it so I can build the latest Castle trunk, I decided to add it to a few of my own projects. What follows is a summary of that experience, and some helpful NAnt tips for notices.

The first step is to set up your development environment. Download the NAnt binaries for the latest release (0.85 as of this writing) and extract those files somewhere on your computer (it went to c:\dev\utils\nant on my computer). Add the NAnt bin folder to your system path so you can run nant.exe from any command prompt.

Now that you have NAnt on your system, you need to create a build file. Build files are XML files which contain the instructions on building your application. Before creating your build file, you need to understand your project’s structure and requirements.

I was writing an NAnt script for my O/R mapper framework, so my project structure was as follows.

CodeSmith
ORMapper.Entities
ORMapper.Services
ORMapper.Services.Log4NetIntegration
ORMapper.Services.NLogIntegration
References
ORMapper.sln

The CodeSmith folder contains CodeSmith code generation templates; the References folder contains DLLs for dependent libraries; ORMapper.* folders contain Visual Studio projects. My build process should build each VS project, adding references as needed, dumping the output in a bin\ directory.

I added a file, default.build (NAnt uses the .build extension by default), to my project directory and started it with the following.

<?xml version="1.0"?>
<project name="ORMapper" default="build" basedir=".">
<description>ORMapper Build</description>
<property name="debug" value="true" />
<property name="build.dir" value="bin" />
<echo message="build.dir: ${build.dir}" />
<echo message="debug: ${debug}" />

The <property /> tags let you define variables which can be overwritten in the command line. The debug property can thus be overwritten to false if you want to compile with debugging off. The build.dir property set the default output (build) directory.

Next, I added a build step (called a target) to initialize the output directory (i.e. create it). This step ensures the bin\ directory exists. I named this target init.

<target name="init">
<mkdir dir="${build.dir}" />
</target>

To ensure the build directory is clean (i.e. it doesn’t include any files from old builds that are not part of new builds), I added a target named clean which deletes any files in it.

<target name="clean" description="remove all generated files">
<delete dir="${build.dir}" failonerror="false" />
<delete>
<fileset basedir="${build.dir}">
<include name="*.*" />
</fileset>
</delete>
</target>

On to the actual compiling of our projects. Since there is a specific build order, I need to be sure the compile instructions are in the appropriate order. NAnt has a <csc /> tag which executes the C# compiler from the .Net Framework. You send instructions to the compiler by adding XML properties to your build script. The below code shows the compilation instructions for the ORMapper.Entities project.

<target name="build" description="debug build (default)" depends="clean,init">
<csc target="library"
output="${build.dir}ORMapper.Entities.dll"
debug="${debug}"
doc="${build.dir}ORMapper.Entities.xml">
<sources basedir="ORMapper.Entities">
<include name="**/*.cs" />
</sources>
<references basedir="References">
<include name="System.dll" />
<include name="WilsonORMapper.dll" />
</references>
</csc>

Take note of the following key elements in the above:

  • The <target /> is dependent on other targets (depends=”clean,init”). This ensures certain build targets are executed prior to this target.
  • Compiler instructions are provided for the type of file to compile, output file name, and so-forth. Note the use of properties — for example, the debug=”${debug}” command will insert the value of the debug property, allowing you to use a single build file for debug and release compilation.
  • To identify the files to include during compilation, you use the <sources /> tag. One or more <include /> tags specify which files in the base directory to compile. (In this case, there are only *.cs files.) By specifying **/ at the start of the name, you tell NAnt to include all files that match the mask in the base directory and all subdirectories.
  • Finally, we specify our references. Framework DLLs (like System.dll) do not need any special path designations; NAnt will find them. For all others, you need to specify the path. In my case, I put reference libraries in a References folder, so I specify that as the base directory. This is where NAnt will find WilsonORMapper.dll.

For subsequent projects, the steps are mostly the same. The only significant difference is in output file names, base directory names, and references. Below is the XML code to compile the other three projects.

<csc target="library"
output="${build.dir}ORMapper.Services.dll"
debug="${debug}"
doc="${build.dir}ORMapper.Services.xml">
<sources basedir="ORMapper.Services">
<include name="**/*.cs" />
</sources>
<references basedir="References">
<include name="System.dll" />
<include name="System.Configuration.dll" />
<include name="System.Data.dll" />
<include name="System.Web.dll" />
<include name="WilsonORMapper.dll" />
<include name="..${build.dir}ORMapper.Entities.dll" />
</references>
</csc>
<csc target="library"
output="${build.dir}ORMapper.Services.Log4NetIntegration.dll"
debug="${debug}"
doc="${build.dir}ORMapper.Services.Log4NetIntegration.xml">
<sources basedir="ORMapper.Services.Log4NetIntegration">
<include name="**/*.cs" />
</sources>
<references basedir="References">
<include name="System.dll" />
<include name="System.Configuration.dll" />
<include name="System.Data.dll" />
<include name="log4net.dll" />
<include name="WilsonORMapper.dll" />
<include name="..${build.dir}ORMapper.Services.dll" />
</references>
</csc>
<csc target="library"
output="${build.dir}ORMapper.Services.NLogIntegration.dll"
debug="${debug}"
doc="${build.dir}ORMapper.Services.NLogIntegration.xml">
<sources basedir="ORMapper.Services.NLogIntegration">
<include name="**/*.cs" />
</sources>
<references basedir="References">
<include name="System.dll" />
<include name="System.Data.dll" />
<include name="NLog.dll" />
<include name="WilsonORMapper.dll" />
<include name="..${build.dir}ORMapper.Services.dll" />
</references>
</csc>

The last thing to do is to copy the reference DLLs to the build directory (the compiler doesn’t do this). This is done using the <copy /> tag, which lets you specify a source fileset and an output directory. In the below, we add instructions to copy all DLL and XML files from the References folder to the build folder. Finally, we add the end tag for the target and project, finishing our NAnt build file.

<copy todir="${build.dir}">
<fileset basedir="References">
<include name="*.dll" />
<include name="*.xml" />
</fileset>
</copy>
</target>
</project>

That’s it! There’s a lot more NAnt can do, and the above just brushes the surface. Hopefully the above example helps you get started with NAnt and take your first steps toward incorporating it into your projects.

RangeValidator: My first attempt to contribute to MonoRail

After following the project quietly for months, I decided to test the waters and see what the CastleProject mind trust thinks of my paltry attempt at a contribution to their otherwise spectacular MonoRail web framework.

My contribution? A simple validation component called RangeValidator. Here’s a synopsis.

Added support for integer and DateTime range validation.

The RangeValidator handles both integer and date/time validation, based on the parameters passed on construction. Supports date/time fields in HTML text fields and supports the handling of enums as integers.

An explanation of each patch file follows…

You can read more and see the patch files by going to the CastleProject JIRA site (issue #COMP-15).

IE7 reverses table rows during Insertion.After

I found yet another interesting bug in IE7, related to using Prototype‘s Insertion.After command to insert additional table rows into an existing table. Apparently, IE7 will reverse the order of the table rows being inserted. As a proof of concept, I’ve set up an ie7 table insert bug test page to prove my point.

Here’s how to duplicate this bug.

  1. Create a new web page (we’ll call it test.htm).
  2. Create a <table> and add a few rows.
  3. Give one row a specific id (such as id="insertAfterThis").
  4. Create a separate web page (call it testdata.htm) with more table rows — just the <tr>...</tr>, nothing else.
  5. Add the following JavaScript to run after the window loads:
    new Ajax.Request('testdata.htm', { method:'get', onSuccess:function(transport) { new Insertion.After('insertAfterThis', transport.responseText); } });
  6. Load the test.htm page in Firefox, and see how the rows are inserted in the order they exist in the testdata.htm file.
  7. Load the test.htm page in IE, and see how the order of the rows is reversed.

It’s quite a frustrating bug, because there’s apparently only two ways to work around it:

  1. Figure out a way to add markup that forces IE to add the rows correctly.
    OR
  2. Have IE receive the table rows from your Ajax call in reverse order (so it’ll reverse it again into the correct order).

Considering we’re not even sure if #1 is possible, it’s a very frustrating bug. Some may say it’s a bug with the Prototype library, but I doubt it, since Prototype is simply inserting text — it has no idea there’s a table involved. IE7 reveals yet another illogical bug.

Debugging Visual Studio 2005 web applications with Firefox

I recently purchased a new laptop (an experience I’ll talk about another time) and got a new desktop at work, so I had the pleasure of re-installing Microsoft Visual Studio 2005 twice in the past two months. (The service pack install was faster, thanks to this tip.) Everything seemed fine, except for one thing: I couldn’t get Visual Studio to recognize Firefox as my default browser, which meant every time I started a web project (debugging or not), Internet Explorer would rear its ugly head.

Getting Visual Studio 2005 to recognize FirefoxThe solution was not to change the Windows default browser (it was already IE) or to set some crazy start action on your web project. It’s more obscure. In VS, go to File / Browse With, add a new browser that points to Firefox, then set that as the default.

OK, it does make sense on some levels, and maybe it’s good that VS can have a different default browser than Windows, but shouldn’t that option be in the Tools / Options window, perhaps under the Environment section, which already has a Web Browser group?

At least I figured it out without breaking (another) mouse.

MonoRail vs Web Client Software Factory (WCSF)

There’s a little flurry of activity going around comparing Castle MonoRail to the Web Client Software Factory (WCSF). Here’s a synopsis.

  • David Hayden, who admittedly is very familiar with WCSF and not very familiar with MonoRail, touts the future of WCSF and the concerns about the lack of resources working on MonoRail in order for it to compete long-term.
  • Scott Bellware talks about evaluating MonoRail for an enterprise application, and he mentions his horrible experience setting up and comprehending WCSF.
  • Hammett, one of the key Castle Project people who admittedly is not familiar with WCSF, talks about the need for WCSF to compete with MonoRail, not the other way around.

Where do I stand? Before answering, I need to explain my history with web development, ASP.Net, and MonoRail.

I started doing web development in the late 1990’s coding by hand and using ColdFusion on the server-side. I migrated to ASP and, ultimately, to ASP.Net, as most people did.

Since the beginning, I’ve had a love/hate relationship with ASP.Net. I love the .Net Framework, C#, and Visual Studio .Net. I hate ViewState, complex web controls that do unpredictable things, and the difficulty injecting JavaScript into ASP.Net web pages (id="ctl00___ctl00___ctl00_ctl00_bhcr_t___TitleBarSearchText", anyone?).

When I stumbled across MonoRail some time in 2006, I liked what I read, but didn’t have the time to explore it further. Towards the middle of 2006 I was working on my own Model-View-Presenter web architecture that was simple but effective, and even started transitioning my only claim to fame, CSFBL, to it. Then, I decided to give MonoRail a try.

I never turned back.

Continue reading

Installing Ubuntu with VirtualPC

For those of you who come to my site looking for help installing Linux on VirtualPC (quite a few, as it is the second most popular post on this site), you’ll know that the big problem revolves around VirtualPC only supporting 16-bit color and most Linux distributions supporting 24-bit color by default. (Why this limitation hasn’t been lifted in VirtualPC yet, I don’t know.)

Microsoft blogger Joe Stagner (who blogs at Joe on .Net) recently posted instructions on how to get an Ubuntu installation to install with 16-bit color — instructions which he in turn found on an Ubuntu blog. Considering the growing popularity of Ubuntu and the cost of VitrualPC 2007 (it’s free), this’ll undoubtedly be helpful to those frustrated by failed Linux installs on VPC.

Revamping the O/R Mapper CodeSmith templates

I took some time over the weekend to make big changes to my CodeSmith templates for Paul Wilson’s O/R Mapper. The changes are significant, so read on if you use them or plan on using them or are curious about them.

The biggest change is that I am removing the static service classes from the templates and distributing them as stand-alone libraries. Of course, source code will be provided along with DLLs. Considering these classes were rarely changed by users, it makes sense. It also makes the library much more accessible to folks who don’t own CodeSmith — now, CodeSmith is used to generate your project’s entity and service classes, not the framework library. This opens the door for more users of Wilson’s O/R Mapper to use this framework.

As a result, in a clean distribution, you would have the following:

  • ORMapper.Services.dll
  • ORMapper.Services.Log4NetIntegration.dll
  • ORMapper.Services.NLogIntegration.dll
  • ORMapper.Entities.dll
  • CodeSmith templates to generate your project’s entity classes, service classes, and unit tests

The first four are the stand-alone libraries (assemblies) previously created as part of the CodeSmith templates (things like DataManager and TransactionManager, both of which are now called DataService and TransactionService). Only the first two are needed (Services and Entities). The Log4NetIntegration and NLogIntegration assemblies are used only if you want to integrate such logging into your service layer (more on that soon).

ORMapper.Entities

The ORMapper.Entities library provides the interfaces and base classes for all your entity objects. Not much has changed in here. All user entity objects derive from EntityBase<T> (read-only entities) or PersistableEntityBase<T> (updateable entities). The IIdentity interface is implemented by your project’s identity classes.

ORMapper.Services

The ORMapper.Services library provides the service classes to do the actual data retrieval and manipulation. It includes the singleton classes DataService (the service that exposes the underlying Wilson O/R mapper engine), TransactionService (simple transaction management), RetrieveService<T> (a generic class for retrieving entity objects), and PersistService<T> (a generic class for persisting entity objects).

A new ServiceLayerConfiguration class allows configuration settings to be provided in your application’s config file. This is a standard configuration handler that you register in your configuration file as follows:

<configuration>
    <configsections>
        <section name="serviceLayer" type="ORMapper.Services.ServiceLayerSettings,ORMapper.Services" />
    </configsections>
    <servicelayer mappingsFile="Mappings.config"
           connectionString="ORMapper"
           logger="" />
</configuration>

The values shown above are default values. Unlike in the past, your Mappings.config file must be copied to your output directory on compilation (in the past it was an embedded resource).

By specifying nlog or log4net as your logger, and by referencing the appropriate integration library assembly with your project, logging will be added automatically using the appropriate logger. This method allows easy switching between libraries and opens the door for adding other logging mechanisms in the future.

NLog integration will use NLog’s standard methods for finding a configuration file (application config, nlog.config, etc.). Log4Net integration will by default look in the application configuration file for settings unless you specify the name of your configuration file in an application settings key named log4net-config-file, as shown below.

<appsettings>
    <add key="log4net-config-file" value="log4net.config" />
</appsettings>

The above would tell log4net to get its configuration from a file named log4net.config (by default in your application’s startup directory).

CodeSmith Templates

There are two CodeSmith templates provided. The first, MappingsFile.cst, will generate the Mappings.config file based on a given database. (Unlike in previous distributions, this one works.) The second, ClassGenerator.cst, will generate your project’s entity and service classes based on a Mappings.config file.

The entity classes are much as they were in the past. Partial classes separate auto-generated code so you can safely add functionality without overwriting it by re-running the templates, and an IIdentity class is created for each entity. Note that the templates will generate uncompilable code if you do not have a primary key specified, so be sure to specify a primary key for each table. (Why you wouldn’t do this anyway, I have no idea.) Entity classes are now generated in a separate project uncluttered by the library code that now exists in the ORMapper.Entities assembly.

The service classes are new, and provide a simpler mechanism to expanding your service layer. Each of your entity classes will be given a (singleton) service class, which will be little more than a strongly-typed wrapper around the RetrieveService<T> or PersistService<T> class. Partial classes are used to separate auto-generated code so you can safely add functionality, much like with entity classes.

How does this change things? In the past, you would retrieve and update a User entity by writing the following code.

User user = Retrieve<user>.RetrieveFirst();
user.IsActive = false;
Persist</user><user>.Save(user);

With the addition of the service layer, you now do this.

User user = UserService.RetrieveFirst();
user.IsActive = false;
UserService.Save(user);

It’s somewhat easier to read, and you can now expand the functionality provided by your UserService class — something you couldn’t do before with the generic classes. Of course, you could still use the generic classes, as the service classes inherit from the base RetrieveService<T> and PersistService<T> classes.

As for the unit test generator that comes with the templates, I made some small changes and, more importantly, successfully ran the tests (even tested it against both logging systems).

Sample Project

I don’t have an official distribution available for this yet, but I do have source code for you to download that includes the source code for all ORMapper.* projects, CodeSmith templates, and a small example project based on a single table that I’ve used as a test framework. Feel free to check it out and provide feedback.

Download the sample project (ZIP, 253kb) – includes everything except the WilsonORMapper.dll assembly, which can be downloaded from www.ormapper.net

When will a final distribution be available? I want to test some more, clean up some code, and add some more XML help comments… and get some feedback from the community. Let me know what you think.

(Please don’t send comments via e-mail — post them here!)