Table of Contents
Castle MonoRail greatly simplifies web development. The Getting Started chapter is going to show you how common tasks are handled and how MonoRail performs most of the repetitive work in web development on your behalf.
Among other things you will learn
About controllers and views and how they relate to each other
How to use layouts and rescues
How to use the
SmartDispatcherController
and the data binder
How to easily create a CRUD page
How Castle ActiveRecord integrates nicely with MonoRail
Although it should go without saying, you are not obliged to use ActiveRecord as your primary data access framework with MonoRail - you can use whatever approach to performing data access you like. That being said if you do use ActiveRecord, there are some provided integrations that might save you some time. Regardless, the framework provided allows you to develop similar integration for your preferred data access approach should you choose to do so.
You can also download the complete example demonstrating the concepts from here.
For the purposes of this chapter we will assume that you have Castle Project assemblies present on your local machine. If you don't, we encourage you to download the MSI installer distribution as it automatically installs all the required assemblies in the Global Assembly Cache, making them available to Visual Studio.
Also, during the course of this chapter will be using a database. For simplicity's sake all discussion surrounding database configuration will refer ot MSSQL Server 2000 however if you prefer to use another database you can select any other database supported by NHibernate and will find very little deviation from the text in this chapter.
As mentioned in the Introduction MonoRail tends to favour convention over configuration and when creating a project structure we encourage you to use a project structure similar to the following to remain in line with these conventions.

You are by no means required to follow these conventions, however they will most likely made development easier for you in the long run. The following sections show you how to create this structure manually or by using the MonoRail project wizard and will further explain the reasoning behind its use.
If you have installed Castle Project using the MSI installer you are likely to have the Visual Studio Integration installed as well. Should that be the case the easiest way to create a new MonoRail project is the use the Castle MonoRail Project Wizard.
The Castle MonoRail Project template described below requires that your Visual Studio installation has Web Project support installed. Visual Studio 2005 does not come with Web Project support out of the box, however there are two options for adding Web Project support to Visual Studio; you can install the Web Project support add in, or alternately you can ensure your Visual Studio installation is upgraded to Visual Studio 2005 Service Pack 1 or later.
To create a MonoRail project using the wizard just perform the following simple steps:
Open Visual Studio and go to → . Under Visual Studio 2005 you will see the following dialog:

Select the
Castle MonoRail Project
template, enter
GettingStartedSample
as the name for your new project then click the
button.
When the wizard starts select the
NVelocity View Engine
from the list of View Engines and click the
button.

Do not enable Windsor Integration or Routing at this stage.
The next step in the wizard will offer the option to create a Test project where unit tests for your new MonoRail application can be created. Ensure the checkbox to create the test project is checked then click the button.
Once all of the above steps have been completed the wizard will create the solution, the web project and the test project and will set up all the basic configuration required for MonoRail to run.
If you haven't installed the Visual Studio integration or would prefer not to use the project wizard and instead would rather set up the project structure by hand you can follow the steps outlined below.
Create an
ASP.Net Web Application
project in Visual Studio 2005. Name the new project
GettingStartedSample
then click the
button.
If the ASP.Net Web Application template is not present in your Visual Studio installation you can create a Class Library project instead.
To make the new project into a MonoRail application you will need to add references to the MonoRail assemblies. For the purposes of our sample application the assemblies listed below will be required.
| Assembly | Description |
|---|---|
| Castle.MonoRail.Framework.dll | Contains the classes that make up the core of the MonoRail framework. |
| Castle.MonoRail.Framework.Views.NVelocity.dll | Defines an NVelocity view engine plugin that will be used for handling views. This will in turn use the NVelocity rendering engine. |
| Castle.Components.Validator.dll | Provides a lightweight validation infrastructure created by Castle. |
| Castle.Components.Binder.dll | Contains the logic for the databinding that is used in MonoRail. |
| Castle.Components.Common.EmailSender.dll | Defines the interface for components that send email. |
| Castle.Components.Common.EmailSender.SmtpEmailSender.dll | Provides an SMTP implementation of the EmailSender that will be responsible for sending email. |
| Castle.Core.dll | Core Castle class library containing functionality used by many projects, including MonoRail. |
| NVelocity.dll | The NVelocity template engine used for rendering NVelocity templates. |
The next step is to create the following folders on the project. Again, this is just a convention we encourage you to follow - you may come up with a more suitable convention for your projects after you get used to the framework so this is no way mandatory.

To configure the application additional information
needs to be added to the
web.config
file; you must register the MonoRail configuration
handler, the MonoRail configuration section, an HTTP
handler and an HTTP module. Each of these will be
discussed further below.
First, add the MonoRail configuration handler:
<configuration> <configSections> <section name="monorail" type="Castle.MonoRail.Framework.Configuration.MonoRailSectionHandler, Castle.MonoRail.Framework" /> </configSections> ...
Next add the
<monorail>
configuration section.
... <monorail> <controllers> <assembly>GettingStartedSample</assembly> </controllers> <viewEngines viewPathRoot="Views"> <add type="Castle.MonoRail.Framework.Views.NVelocity.NVelocityViewEngine, Castle.MonoRail.Framework.Views.NVelocity" /> </viewEngines> </monorail> ...
This is the section that will contain all the
configuration that controls setting up and
customizing the behavior of MonoRail itself. The
elements included for our sample above are the
<controllers>
node that defines the list of assemblies that
contain the controllers for the application and the
<viewEngines>
node the specifies the view engine that will handle
rendering the view templates in the application.
There are more configuration options available in
the
Configuration
chapter, however the elements above are sufficient
for this sample.
It should be noted at this point that we have chosen the NVelocity view engine for rendering the views in our application. NVelocity is a very simple template engine that supports conditional statements, assignments, array creation, and iteration over collections that provides all the functionality you will generally need during the rendering of a view. You can learn more about NVelocity (and how we have improved it) on its page .
Next we will add a couple of HTTP Handlers under the
<httpHandlers>
node in the
<system.web>
section of the
web.config
.
... <system.web> <httpHandlers> <add verb="*" path="*.castle" type="Castle.MonoRail.Framework.MonoRailHttpHandlerFactory, Castle.MonoRail.Framework" /> <add verb="*" path="*.vm" type="System.Web.HttpForbiddenHandler"/> </httpHandlers> ... -
The first HTTP Handler is responsible for routing
incoming requests to MonoRail based on their
extension. The example above indicates that incoming
requests ending in
.castle
should be handled by MonoRail. If you prefer another
extension such as
.rails
you can simply change the value of the
path
attribute to your preferred value.
The second handler is present as a security
consideration. It is important to to be aware that
if a file in a web folder is accessed from the
outside world its contents will normally be sent
directly to the calling browser. To prevent the
source for your view files from being visible to the
outside world an
HttpForbiddenHandler
needs to be configured for the file extension of any
files you don't want want to be directly accessible.
In this case, because we chose to use the NVelocity
view engine and our view templates use the
.vm
extension this is the extension we have associated
with the
HttpForbiddenHandler
.
Finally you need to register the MonoRail HTTP
module that handles incoming requests into the
<httpModules>
node, which is also found in the
<system.web>
section.
... <httpModules> <add name="monorail" type="Castle.MonoRail.Framework.EngineContextModule, Castle.MonoRail.Framework" /> </httpModules> </system.web> </configuration>
That's it - the configuration is now complete. Next we will start exploring controllers, actions and views.
For all MonoRail MVC applications the entry point for each request is the controller - this is the single largest deviation from the approach taken in Web Forms development where the entry point is the page itself. With MonoRail the controller is executed in response to a request where an action decides whether or not it should render a view and, if so, which one to render. As a result the controller has control over the application flow rather than the pages/views. This returns views to their original role: to present information - nothing more and nothing less.
It is important at this point to understand the relationship between the structure of the MonoRail URL and how it relates to the controller and the method to execute on the controller. A common URL to a MonoRail web page might look like the following:
http://www.some-server.net/home/index.castle
| | | | |
+-------------------------+----+-----+------+
^ ^ ^ ^
| | | |
Server | | |
| | |
Controller | |
| |
Action |
|
Extension
In the diagram above the extension
.castle
indicates that MonoRail should handle the request,
home
specifies the controller to use and
index
indicates that the
Index
action method on the controller should be executed. Since
MonoRail prefers convention over configuration the default
behaviour is for MonoRail to use the controller name in the
URL to locate the controller class by appending the word
Controller
to the end, so in the event that the controller name
home
is found in the URL the controller class used in response to
this request will be the
HomeController
class.
It is now time to create your first controller. If you
have used the wizard to create the project, a
HomeController
class will already have been created for you. If it is
not already present you should create a new class named
HomeController
in the
Controllers
folder of your project.

Inside the
HomeController
class the following code should be present. If you have
just created the class file yourself you should add the
code now.
namespace GettingStartedSample.Controllers { using System; using Castle.MonoRail.Framework; [Layout("default"), Rescue("generalerror")] public class HomeController : SmartDispatcherController { public void Index() { } } }
This is a pretty basic sample for the moment. Don't worry if you don't understand it at this point; we'll explain the code and surrounding concepts in more detail in the next section.
An
action
is a public non-static method exposed by a controller
class. The controller shown in the previous section
exposes just one action,
Index
which is empty, however due to MonoRail's convention
over configuration approach the controller will render a
specific view template by default.
The view template chosen by MonoRail is affected by the
view engine chosen during the configuration phase of the
project. Since we chose to use the NVelocity view engine
all of our view templates will end with the
.vm
extension.
By default, the specific view template file chosen for
rendering is based on the name of the action being
executed and the folder in which the view template is
located is based on the name of the controller handling
the request. As a result the default view template
chosen by the
Index
action on the
HomeController
will be
index.vm
located under the
/views/home
folder in the project.
This behavior can be overridden in several ways that are covered later in this guide.
If the view template
index.vm
does not yet exist in the
/views/home
folder of your project you should create it now.

To illustrate a point we will make the content of the
home.vm
file nice and simple to start with. If the
home.vm
file does not contain the content below just copy and
paste it in there.
<p> Hello world from my first action. </p>
You can make Visual Studio use the HTML editor for
.vm
files. Use the
option from the context menu.
This is a fairly simple view; note that in the template we did not include the html or body tags. This is because we will leave those to the layout to render, which we will cover next.
When we created the
HomeController
you may have noticed the
Layout
and
Rescue
attributes were specified on the class.
[Layout("default"), Rescue("generalerror")] public class HomeController : SmartDispatcherController
These attributes define the following:
The
Layout
attribute indicates that every view rendered
by this controller to should be wrapped in
the content from the
default.vm
template in the
/layouts
folder of the project.
The
Rescue
attribute tells MonoRail that if any
unhandled exception is thrown by the an
action on the
HomeController
a special
rescue
view template called
generalerror.vm
in the
/rescues
folder of the project should be rendered.
Layouts and rescues can minimize the amount of work you do when building view templates and allow your view templates to be better encapsulated.
A
layout
defines the outer content of a rendered view and has
some similarities to a master page. Your project can
have as many layouts as you like - the layout files
simply need to be placed into the
/layouts
folder of the project.
Our first layout is very simple, and should be saved as
default.vm
on the
/layouts
folder.
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd"> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" /> <title>Layout</title> <link rel="stylesheet" href="$siteRoot/Content/css/base.css" /> </head> <body> $childContent </body> </html>
At this point some explanation of the template above is in order.
The
$childContent
variable defines where the content of the view being
rendered will be included on the layout.
The
$siteRoot
variable will represent the root of the application. If,
for example, the web application is using a virtual
directory it will be the name of the virtual directory.
Under normal circumstances, however, it will simply
evaluate to "
/
" .
If you created the application with the wizard, just hit
→
or
F5
in Visual Studio to start a debug session. On the
browser, go to the application URL and enter the
controller name and the action name plus the extension
you have selected, eg.
http://localhost:8081/home/index.castle
. The browser should render the view content with the
layout applied:

If you view the source of the page in your browser you should see well formed XHTML content that includes the content defined in the layout as well as the content rendered by the view.
Should you wish to run the application using
Microsoft Internet Information Services, you must
associate the extension you selected (i.e.
.castle
) with the ASP.Net ISAPI. For more information on
how to do this please see the chapter entitled
Installation
.
As shown by the excessively simple view example used
earlier, views are pretty useless unless you can pass
custom values to them for rendering at runtime. The main
mechanism for transferring values from the controller to
the view is the
PropertyBag
.Values placed in the
PropertyBag
will be made available to the view template, however
each view engine will provide access to these values
differently. Since we're using the NVelocity view engine
the values in the
PropertyBag
will be exposed as NVelocity variables which begin with
a
$
symbol in the NVelocity view template.
To demonstrate this principle let's create another
action on our
HomeController
.
[Layout("default"), Rescue("generalerror")] public class HomeController : SmartDispatcherController { public void Index() { } public void DataToTheView() { PropertyBag["name"] = "John Doe"; PropertyBag["today"] = DateTime.Now; RenderView("data"); } }
The new
DataToTheView
action uses the
PropertyBag
to pass data to the view. It then invokes the built-in
RenderView
method on the controller to specify that the
/views/home/data.vm
view template should be rendered instead of the default
/views/home/datatotheview.vm
template.
Now let's create the
data.vm
template in the
\views\home
folder.
<h3> Hello $name! </h3> <h4> Today is $today.ToShortDateString() </h4>
To see the result of the change run the application and
visit
home/datatotheview.castle
in your browser.
In an earlier step we specified that our controller uses a rescue. A rescue is a special purpose view template that will be rendered only in the event that an exception is thrown while executing an action on a controller. Lets create the rescue view to report errors nicely and see how it works.
First, create a view template named
generalerror.vm
in the
\views\rescues
folder with the following content.
#macro(showexception $exc) <b>$exc.GetType().Name</b> Message: $exc.Message #if($exc.StackTrace) StackTrace: $exc.StackTrace #end #if($exc.InnerException) <b>Inner exception:</b> #set($inner = $exc.InnerException) #showexception($inner) #end #end <h2>Unexpected error happened</h3> <p> This is the rescue page. See the exception details below </p> <pre> #set($exception = $context.LastException) #showexception($exception) </pre>
There's quite a bit of logic in this template, but we won't get into the details now. If you want to explore these features in more detail see the NVelocity documentation.
Now let's create a new action that will throw an exception so we can see the rescue template in action.
[Layout("default"), Rescue("generalerror")] public class HomeController : SmartDispatcherController { public void Index() { } public void ErroneousAction() { throw new Exception("I'm a bad bad action."); } public void DataToTheView() { PropertyBag["name"] = "John Doe"; PropertyBag["today"] = DateTime.Now; RenderView("data"); } }
If you now run the application and go to
home/erroneousaction.castle
in your browser the
ErroneousAction
method will throw an exception as we specified and the
rescue will be displayed.
Although the controller we've been using so far in our
sample application extends from
SmartDispatcherController
we haven't yet used any of its more advanced capabilities.
The
SmartDispatcherController
base class, as the name implies, provides some "smart"
functionality, including binding query string values, form
values and even cookies to the parameters of action methods
when they are invoked. This includes both simple types and
custom, complex types as will be discussed in the following
sections.
Simple types can be bound to action parameters with no
special configuration other than to ensure your
controller is derived from the
SmartDispatchController
class. For example, suppose you have the following HTML
form on a view:
<form action="/home/saveinformation.castle" method="post" > <input type="text" name="name" value="John doe" /> <input type="text" name="age" value="30" /> <input type="text" name="dob" value="07-16-1979" /> <input type="submit" value="Send" /> </form>
The form action directs the post to the
HomeController
class'
SaveInformation
action method. This method can be constructed in in such
a way that it will expect the values from the submitted
HTML form as parameters:
public class HomeController : SmartDispatcherController { public void Index() { } public void SaveInformation(String name, int age, DateTime dob) { // work work work // Send the user back to the index RedirectToAction("index"); } }
In the above example each of the form fields will be
data-bound to the method parameter of the same name on
the
SaveInformation
method.
Using this automatic data binding feature reduces the effort required to access the values submitted by the view. It is still possible to access the values from the HTML form without data binding through use of the Params or Form name-value collections exposed by the controller, however using data binding is cleaner and simpler. The code sample below provides the same functionality as the example above - we will leave it to you to decide which is clearer and easier to use.
public class HomeController : SmartDispatcherController { public void Index() { } public void SaveInformation() { String name = Params["name"]; int age = Convert.ToInt32(Params["age"]); DateTime dob = Convert.ToDateTime(Params["dob"]); // work work work // Send the user back to the index RedirectToAction("index"); } }
The
SmartDispatchController
is capable of handling a wide range of built-in .NET
types. For further details on the types supported refer
to the documentation on
Data Binding
in the
Controllers
chapter.
Along with data binding simple value types the
SmartDispatcherController
provides features to data bind custom and arbitrarily
complex objects. In order to illustrate how this can be
achieved we will create a new controller that presents a
contact form designed to allow a user to contact a
department in a fictitious company.
First, create a new controller named
ContactController
and add two empty actions;
ContactForm
and
SendContactMessage
.
namespace GettingStartedSample.Controllers { using System; using Castle.MonoRail.Framework; [Layout("default"), Rescue("generalerror")] public class ContactController : SmartDispatcherController { public void ContactForm() { } public void SendContactMessage() { } } }
Next, in the
/models
folder create a class named
Contact
- this class will be used to represent the contact
information submitted by the user.
public class Contact { private string from; private string area; private string subject; private string message; public string From { get { return from; } set { from = value; } } public string Area { get { return area; } set { area = value; } } public string Subject { get { return subject; } set { subject = value; } } public string Message { get { return message; } set { message = value; } } }
Now create the view to be displayed by the
ContactForm
action. The template file
contactform.vm
should be created in the
\views\contact
folder and must contain the following code:
<h2>Contact us!</h2> <p> We are interested in hearing from you. </p> <form action="contact/sendcontactmessage.castle" method="post"> <p> From: $FormHelper.TextField("contact.from") </p> <p> Area: $FormHelper.TextField("contact.area") (Sales, Support) </p> <p> Subject: $FormHelper.TextField("contact.subject", "%{size='30'}") </p> <p> Message: <br/> $FormHelper.TextArea("contact.message", "%{cols='35', rows='6'}") </p> <br /> <p> <input type="submit" value="Send the message" /> </p> </form>
In the code above we have made use of the
FormHelper
helper class to generate an HTML form with the following
source:
<h2>Contact us!</h2> <p> We are interested in hearing from you. </p> <form action="contact/sendcontactmessage.castle" method="post"> <p> From: <input type="text" name="contact.from" /> </p> <p> Area: <input type="text" name="contact.area" /> (Sales, Support) </p> <p> Subject: <input type="text" name="contact.subject" size="30" /> </p> <p> Message: <br/> <input type="textarea" name="contact.message" cols="35" rows="6" /> </p> <br /> <p> <input type="submit" value="Send the message" /> </p> </form>
Further information on the
FormHelper
helper and on helpers in general can be found in the
chapter entitled
Helpers
.
At this point it is important to note that the name of
each generated field on the HTML form has been prefixed
with the text
contact
followed by a dot and then the name of the property of
the form (eg.
contact.from
in the case of the "From" field in our example). This
indicates that each of the fields belong to a single
group of data that will be bound to an object by the
data binder.
Finally, change the parameters of the
SendContactMessage
action on the
ContactController
to indicate to the MonoRail binder that it should bind
the form data prefixed with
contact
to the
Contact
class created earlier.
public void SendContactMessage([DataBind("contact")] Contact theContact) { // Pretend to save the contact ... // ..work work work.. // Now lets add the contact to the property bag // so we can render a nice message back to the user PropertyBag["contact"] = theContact; RenderView("confirmation"); }
As you can see, a parameter named
theContact
has been added to the
SendContactMethod
action which has been decorated with the
DataBindAttribute
. The
DataBindAttribute
has been initialized with a string parameter that tells
the binder that it should attempt bind the form values
prefixed with
"contact."
to the properties of the
theContact
parameter that share the same name as the part of the
form element name that appears after the text
"contact."
.
While we're here we might as well create the
confirmation
view as well.
<p> Thanks $!contact.from, we have received your message and will get back to your shortly. </p>
Now if you visit
/contact/contactform.castle
in your browser you should be able to view the results
of your work. Fill the form elements, submit it and you
should see the name you submitted in the
from
field on the form.


In order to explore some more realistic scenarios you will face during daily development lets take a look at using ActiveRecord for reading and writing data stored in a database. The Castle ActiveRecord project is an implementation of the ActiveRecord pattern for .NET that uses Nhibernate to communicate with the database.
To add ActiveRecord support to the sample project we will reference the required assemblies, set up a database and finally configure and initialize ActiveRecord.
For more information on ActiveRecord see the ActiveRecord documentation .
First, add references to the assemblies required to use ActiveRecord in your project :
Castle.ActiveRecord.dll
Castle.DynamicProxy.dll
Iesi.Collections.dll
log4net.dll
NHibernate.dll
Now we need to add the ActiveRecord configuration
elements to the
web.config
in the project.
<configuration> <configSections> ... <section name="activerecord" type="Castle.ActiveRecord.Framework.Config.ActiveRecordSectionHandler, Castle.ActiveRecord" /> </configSections> <activerecord isWeb="true"> <config> <add key="hibernate.connection.driver_class" value="NHibernate.Driver.SqlClientDriver" /> <add key="hibernate.dialect" value="NHibernate.Dialect.MsSql2000Dialect" /> <add key="hibernate.connection.provider" value="NHibernate.Connection.DriverConnectionProvider" /> <add key="hibernate.connection.connection_string" value="Data Source=.;Initial Catalog=test;Integrated Security=SSPI" /> </config> </activerecord> ...
The configuration above defines the section handler that tells ActiveRecord how to find and interpret the ActiveRecord configuration information followed by the configuration section itself.
For our sample we've used SQL Server 2000, however since ActiveRecord uses NHibernate it can support any database dialect that NHibernate supports. If you are using a different database you want to change the dialect please refer to the ActiveRecord Xml Configuration Reference for further information.
The example configuration section we've defined contains
information that tells ActiveRecord the type of database
we're using and specifies the connection string to a
database named
test
. You will need to make sure that the database exists or
will need to change the connection string to specify
another database.
If you change the connection string to another database it must already exist and the tables required to support the sample project will be added to it.
Next, we'll create two new model classes that will
represent products and suppliers. The classes should be
created in the
/models
folder of the project. First, create the
Supplier.cs
class to represent the supplier:
namespace GettingStartedSample.Models { using System; using Castle.ActiveRecord; using NHibernate.Expression; [ActiveRecord] public class Supplier : ActiveRecordBase { private int id; private String name; [PrimaryKey] public int Id { get { return id; } set { id = value; } } [Property] public string Name { get { return name; } set { name = value; } } /// <summary> /// Returns the Suppliers ordered by Name /// </summary> /// <returns>Suppliers array</returns> public static Supplier[] FindAll() { return (Supplier[]) FindAll(typeof(Supplier), new Order[] { Order.Asc("Name") }); } } }
Then create the
Product.cs
class to represent the product:
namespace GettingStartedSample.Models { using System; using Castle.ActiveRecord; [ActiveRecord] public class Product : ActiveRecordBase { private int id; private String name; private decimal price; private Supplier supplier; [PrimaryKey] public int Id { get { return id; } set { id = value; } } [Property] public string Name { get { return name; } set { name = value; } } [Property] public decimal Price { get { return price; } set { price = value; } } [BelongsTo("SupplierId")] public Supplier Supplier { get { return supplier; } set { supplier = value; } } public static Product[] FindAll() { return (Product[]) FindAll(typeof(Product)); } public static Product FindById(int id) { return (Product) FindByPrimaryKey(typeof(Product), id); } } }
You may notice that the classes have been decorated
with the
[ActiveRecord]
attribute and each of their properties have been
decorated with attributes such as
[PrimaryKey]
,
[Property]
and
[BelongsTo]
. These, and other elements of these classes are
specific to ActiveRecord; to gain a better
understanding of these features you should refer to
the
ActiveRecord documentation
.
The last step is to initialize ActiveRecord. Ideally in
MonoRail this is done in the class associated with the
global.asax
file. If no
global.asax
create it now with the following content:
<%@ Application Inherits="GettingStartedSample.GlobalApplication" %>
Now create the
GlobalApplication
class the
global.asax
refers to by adding a
GlobalApplication.cs
file to the project.
namespace GettingStartedSample { using System; using System.Web; using Castle.ActiveRecord; using Castle.ActiveRecord.Framework.Config; using GettingStartedSample.Models; public class GlobalApplication : HttpApplication { public GlobalApplication() { } public void Application_OnStart() { ActiveRecordStarter.Initialize(ActiveRecordSectionHandler.Instance, new Type[] { typeof(Supplier), typeof(Product) }); // If you want to let ActiveRecord create the schema for you: ActiveRecordStarter.CreateSchema(); } public void Application_OnEnd() { } } }
The
GlobalApplication
class inherits from
HttpApplication
and implements
Application_OnStart
where code to be executed before the application begins
for the first time will be located. Into this method we
have added the
ActiveRecordStarter.Initialize
method call to initialize ActiveRecord.
In the sample above we've added a call to
ActiveRecordStarter.CreateSchema
which will create the required tables in the
database to match the ActiveRecord classes defined
earlier. After the application has been started for
the first time and the database schema has been
populated this line should be commented out.
There are several ways of creating the pages that handle Create, Retrieve, Update and Delete (CRUD) functionality in MonoRail, the simplest of which is using ActiveRecord Scaffolding, which works well for prototyping . Since this application is simply to demonstrate some of the features of MonoRail we will use Scaffolding to get up and running quickly, however later we will look at a more sophisticated solution that would be more suitable for production applications.
Enabling ActiveRecord only requires that we add a couple of additional assemblies and create a very basic controller.
First, add references to the assemblies required for ActiveRecord Scaffolding support:
Castle.MonoRail.ActiveRecordScaffold.dll
Castle.MonoRail.ActiveRecordSupport.dll
Next, create the controller that will provide the
CRUD functions for the
Supplier
class:
namespace GettingStartedSample.Controllers { using System; using Castle.MonoRail.Framework; using GettingStartedSample.Models; [Scaffolding(typeof(Supplier))] public class SupplierController : Controller { } }
You may notice that the controller does not contain
any code at all. This is because the addition of the
ScaffoldingAttribute
decoration on the controller class tells MonoRail
that this is a Scaffolding controller so all the
additional functionality is automatically provided.
If you use Scaffolding for your CRUD functionality
that is it - all the features have automatically
been added. If you visit the url
/supplier/list.castle
you will see that the list page is automatically
provided along with all the navigation features and
links to add, remove and modify supplier data in the
database.


In the previous section we looked at the creation of
basic CRUD pages to access
Supplier
data using Scaffolding; now we'll look at a more
flexible approach. In this example we will manually
create the controller actions and views to provide a
more sophisticated solution. To keep things simple we'll
reuse the
Supplier
and
Product
model classes we created earlier and will now develop
the CRUD pages for the
Product
class.
First, create the basis of the
ProductController
class using the following code:
namespace GettingStartedSample.Controllers { using System; using Castle.MonoRail.Framework; using GettingStartedSample.Models; [Layout("default"), Rescue("generalerror")] public class ProductController : SmartDispatcherController { } }
Into this controller we will add the action that will present the list of products along with the actions that control adding, updating and deleting products.
First, lets look at creating the the
List
action on the controller. This action will present a
list of the
Product
data from the database.
public void List() { PropertyBag["products"] = Product.FindAll(); }
The call to the static
FindAll
method on the
Product
in the code above gets all the
Product
items from the database which are then made
available to the view by passing them to the
PropertyBag
. The
FindAll
method, amongst others, is exposed by the
ActiveRecordBase
class that we used as the base class for our
Product
.
Next, build the view to go with the
List
action by creating the
list.vm
file with the following content in the
/views/product
folder.
<h3>Product list</h3> <p> <a href="new.castle">Create new Product</a> </p> <table width="100%" border="1" cellpadding="2" cellspacing="0"> <tr> <th>Id</th> <th>Name</th> <th>Supplier</th> <th> </th> </tr> #foreach($product in $products) <tr> <td align="center">$product.Id</td> <td align="center">$product.Name</td> <td align="center">$product.Supplier.Name</td> <td align="center"> <a href="edit.castle?id=${product.Id}">Edit</a> | <a href="delete.castle?id=${product.Id}">Delete</a> </td> </tr> #end </table>
At this stage you can see the results of the
List
action and view by visiting the
/product/list.castle
url in your browser.

To create a new product we will use two actions; one
that will present the form on which the user can
provide the information about the new product and
another that will store the provided data into the
database. First, create the
New
action that will present the user with the form.
public void New() { PropertyBag["suppliers"] = Supplier.FindAll(); }
This action, apart from displaying the form to the user provides all the existing suppliers available to the view so it can populate a select element to choose a supplier from.
Now add the view associated with the
New
action by creating the
new.vm
file in the
/views/product
folder and populate it with the following content:
<h3>New Product</h3> #if($Flash.error) <p style="color: red; font-weight: bold;"> $Flash.error </p> #end <form action="create.castle" method="post"> <p> Name: $FormHelper.TextField("product.name") </p> <p> Price: $FormHelper.TextFieldFormat("product.price", "000.00") </p> <p> Supplier: $FormHelper.Select("product.supplier.id", $suppliers, "%{value='Id', text='Name'}") </p> <hr/> <p> <input type="submit" value="Create" /> </p> </form>
Finally, add the
Create
action to handle the data submitted from the form by
the user.
public void Create([DataBind("product")] Product prod) { try { prod.Create(); RedirectToAction("list"); } catch(Exception ex) { Flash["error"] = ex.Message; Flash["product"] = prod; RedirectToAction("new"); } }
This method is using data binding to bind the form
data into the
prod
parameter which then uses the
Create
method from the
ActiveRecordBase
base class of the
Product
class. The
Create
method will add the
Product
into the database. Once the
Product
has been created this action will redirect the user
back to the
List
action created earlier.
You can now test your work by accessing the
New
action using the
/product/new.castle
url.

Building the update functionality is very similar to
the process used when building the create feature.
Again there will be two actions; one that presents a
form for editing a
Product
and another for saving the changes after the form
has been submitted.
First, create the two actions for edit and update:
public void Edit(int id) { PropertyBag["product"] = Product.FindById(id); PropertyBag["suppliers"] = Supplier.FindAll(); } public void Update([DataBind("product")] Product prod) { try { prod.Update(); RedirectToAction("list"); } catch(Exception ex) { Flash["error"] = ex.Message; Flash["product"] = prod; RedirectToAction("edit", "id=" + prod.Id); } }
The
Edit
action will present the form to the user and provide
all the relevant data to the form. The
Update
action will handle saving the changes submitted by
the user and redirecting the ser back to the
List
action.
We can also overload the
Edit
action method. This overload will be used
specifically when an error is caught by the
Update
action. In the catch handler the action redirects
the user back to the edit page so they can correct
any errors. Also of note is the
FlashBinder
which allows you to bind data to parameters from the
Flash
rather than from Request or Post data.
public void Edit(int id, [FlashBinder] Product product) { PropertyBag["suppliers"] = Supplier.FindAll(); }
Next, we'll create the edit view. The
edit.vm
template is very similar to the
new.vm
template created in the last section; it will be
responsible for displaying the form for editing the
product.
<h3>Edit Product</h3> #if($Flash.error) <p style="color: red; font-weight: bold;"> $Flash.error </p> #end <form action="update.castle" method="post"> $FormHelper.HiddenField("product.id") <p> Name: $FormHelper.TextField("product.name") </p> <p> Price: $FormHelper.TextFieldFormat("product.price", "000.00") </p> <p> Supplier: $FormHelper.Select("product.supplier.id", $suppliers, "%{value='Id', text='Name'}") </p> <hr/> <p> <input type="submit" value="Update" /> </p> </form>
In the code above we've used the
FormHelper
which is clever enough to populate the fields and
select the correct item on the form's
select
element.
The last in our CRUD series, the delete step is the
easiest one; simply add the
Delete
action to the controller with the following code.
public void Delete(int id) { Product product = Product.FindById(id); product.Delete(); RedirectToAction("list"); }
This action will be called when the user clicks the delete link on the list page. It gets the if of the product using automatic data-binding, finds the product, deletes it and redirects back to the list page.
The Getting Started chapter gave you a very quick introduction to many of MonoRail's features and and how to use them, however there is much more to learn. To get a better feel for MonoRail you can explore the samples, try some experiments of your own, and consult documentation on the various features covered here for much more in-depth information.