Monday, December 18, 2006

Ajax Scaffolding with Castle MonoRail and C#

Download Sources, and view demos

Goal

Let's say we need to write an application very fast, that can do the basic CRUD operations for a Product. Ruby on rails (www.rubyonrails.org) came up with the excellent idea of scaffolding, and the idea was ported into the Castle Monorail project (www.castleproject.org). However, the default generator both in ROR and in MR, do not generate ajax based code. For ROR the solution is at: www.ajaxscaffold.com but nothing so far for MR. So I decided to take matters in my own hands...

Where do we start from?

The excellent generator by Marc Andre Cournoyer can generate projects, controllers, migrations, model etc and the templates can be changed to suit one's needs extremely easy. Starting from Marc Andre's generator I configured the templates to generate AJAX based code.

Let's see how it works, step by step starting from nothing.

Step 1: we make a new MonoRail project

Using latest RC2, and VS.NET 2005, we generate the project using the wizard inside VS.NET.



resulting in:



Then we edit the web.config and we create a new database called Products.

Step 2: we generate the persistent activerecord class and the database table

Starting for a basic Castle Monorail/ActiveRecord project, we start by creating the Product class, and then generating the database table corresponding to it. In the application's root folder, we type: generate model Product Name Description Price and it generates the code for us:


obtaining the following code (click view all files on the web project, and include the new file in project)

///
/// An instance of this class represents a Product.
///

[ActiveRecord]
public class Product : ActiveRecordValidationBase {
#region Fields
private int _id;
private string _name;
private string _description;
private string _price;
#endregion

#region Persistent properties
[PrimaryKey]
public int Id {
get { return _id; }
set { _id = value; }
}
[Property]
public string Name {
get { return _name; }
set { _name = value; }
}
[Property]
public string Description {
get { return _description; }
set { _description = value; }
}
[Property]
public string Price
{
get { return _price; }
set { _price = value; }
}
#endregion

internal static IList FindAll()
{
return FindAll(typeof(Product));
}

internal static Product Find(int id)
{
return FindByPrimaryKey(typeof(Product), id) as Product;
}

}

To generate the database schema we just use:

ActiveRecordStarter.CreateSchema();

and we have the table in our database.

Step 3: we generate the Ajax Scaffolding code

We type in our command window: generate scaffold Product and it will generate all the needed code dor adding, listing, editing and deleting products, using Ajax:


now we include the generated files in our solution from Visual Studio 2005 (right click, Include in project):



Step 4: we run the code. It works!

We start the application:

then we add:


after adding two products:

now viewing in place:


and editing:


and the same with deleting.

What about validations?

Let's say that the name and the price are required. We modify the Product class, to add the validation attributes:

///
/// An instance of this class represents a Product.
///

[ActiveRecord]
public class Product : ActiveRecordValidationBase {
#region Fields
private int _id;
private string _name;
private string _description;
private string _price;
#endregion

#region Persistent properties
[PrimaryKey]
public int Id {
get { return _id; }
set { _id = value; }
}
[Property,ValidateNotEmpty("Name is not optional.")]
public string Name {
get { return _name; }
set { _name = value; }
}
[Property]
public string Description {
get { return _description; }
set { _description = value; }
}
[Property, ValidateNotEmpty("Price is not optional.")]
public string Price
{
get { return _price; }
set { _price = value; }
}
#endregion

internal static IList FindAll()
{
return FindAll(typeof(Product));
}

internal static Product Find(int id)
{
return FindByPrimaryKey(typeof(Product), id) as Product;
}

}

and restart the application:



How hard is it to extend the product?

Let's say that we've just found out that besides the Name, Description and Price, we also need to add to each product a serial number. We modify the Product class, adding the new field and property:

private string _serialNumber;

...

[Property]
public string SerialNumber
{
get { return _serialNumber; }
set { _serialNumber = value; }
}

Now, we recreate the table:

ActiveRecordStarter.CreateSchema();

and start the application:

and


so it doesn't need any modification in the code or views.

What about relationships?

Let's consider that we want each product to be part of a Category. Following the steps above we create a class Category:

[ActiveRecord]
public class Category : ActiveRecordValidationBase {
#region Fields
private int _id;
private string _name;
private IList products = new ArrayList();
#endregion

#region Persistent properties
[PrimaryKey]
public int Id {
get { return _id; }
set { _id = value; }
}
[Property]
public string Name {
get { return _name; }
set { _name = value; }
}

[HasMany(typeof(Product))]
public IList Products
{
get { return products; }
set { products = value; }
}

#endregion

public override string ToString()
{
return this.Name;
}

and modify the product to be part of a Category by adding:

private Category _category=null;
...
[BelongsTo("CategoryID")]
public Category Category
{
get { return _category; }
set { _category = value; }
}

we regenerate the database, obtaining:


Using the generator we also generate the ajax scaffolding code to add/edit/delete and list categories. Now we add two categories:


and going to add new products we'll also have the category combobox, without us making any modification in the code or the views:


Conclusion

As it can be seen above, with very little effort, in 10 minutes we can create an easy to extend ajax application (thanks to Castle Project and Marc Andre's generator). The code is quite clear and easy to debug and extend, and since it is ajax based it can be easily integrated in existing web applications.