MonoRail


Table of Contents

1. Introduction
1.1. Overview
1.2. Background
1.2.1. What is MVC
Model
View
Controller
1.3. Why Use MonoRail
1.3.1. Convention Over Configuration
1.3.2. Container support
1.3.3. Testability
1.4. How It Works
1.5. Licence Information
1.6. Support
2. Getting Started
2.1. Introduction
2.2. Requirements
2.3. Creating the Project Skeleton
2.3.1. Using the MonoRail Project Wizard
2.3.2. Creating the Project Manually
Create the Project
Reference the MonoRail Assemblies
Create the Project Folders
Configuration
2.4. Controllers, Actions and Views
2.4.1. Your First Controller
2.4.2. The Index Action and View
2.4.3. Setting the Layout and Rescue
2.4.4. Creating the Layout
2.4.5. Seeing the Results
2.4.6. Passing Values to a View
2.4.7. Creating a Rescue
2.5. Data Binding
2.5.1. Simple Parameters
2.5.2. Complex Objects
2.6. Integrating with ActiveRecord
2.6.1. Adding Assemblies
2.6.2. Configuration
2.6.3. Building the Model
2.6.4. Initializing the Handler
2.6.5. ActiveRecord Scaffolding
Adding Assemblies
Creating the Controller
Viewing the Result
2.6.6. Creating CRUD Pages Using DataBind
Presenting the List
Creating
Updating
Deleting
2.7. Final Comments
3. Installation
3.1. Introduction
3.2. Running Under IIS
3.3. Using Casini
3.4. Mono with XSP
3.5. Mono with Apache
3.5.1. Configuration
3.5.2. Apache Httpd2
3.5.3. Application Deployment
3.6. Deploying to a Shared Host
4. Configuration
4.1. Introduction
4.2. Formal Definition
4.2.1. The monorail Node
4.2.2. The controllers Node
4.2.3. The viewcomponents Node
4.2.4. The viewEngine Node
The additionalSources Node
4.2.5. The services Node
4.2.6. The extensions Node
4.2.7. The routing Node
5. Controllers
5.1. Introduction
5.2. Naming Convention
5.3. Areas
5.4. Actions
5.4.1. Default Action
5.5. Redirecting
5.6. Other Useful Properties
5.7. Data Binding
5.7.1. The SmartDispatchController
5.7.2. Simple Parameter Binding
DateTime Properties
Nullable Support
Array Support
5.7.3. Custom Binding
The DataBind Attribute
Nested Objects
Array Support
Generic Lists
Setting the Binding Source
Defining Accessible Properties
Binding Errors
BindObject and BindObjectInstance
Supported Types
FormHelper
5.8. Wizards
5.8.1. Wizard Controllers
5.8.2. Wizard Action Provider
5.8.3. Steps
5.8.4. Nested Actions
5.8.5. DoNavigate
5.8.6. Conditional Steps
5.8.7. The WizardHelper
5.8.8. Windsor Integration
6. Views
6.1. Introduction
6.2. Folder Structure Convention
6.3. Selecting a View to Render
6.4. Passing Values to a View
6.4.1. The PropertyBag
6.4.2. Flash
6.5. Shared Views
6.6. Cancelling a View
6.7. Accessing Values Passed by the Controller
6.8. Javascript and Ajax
6.8.1. Javascript Generation
6.8.2. JSON
6.8.3. The JSON Binder
7. View Components
7.1. Introduction
7.2. Creating a View Component
7.3. Using View Components
7.4. Passing Parameters
7.5. Block and Nested Sections
7.6. Built In View Components
7.6.1. CaptureFor
7.6.2. SecurityComponent
7.6.3. DiggStylePaginationComponent
Basic Usage
Customizing Captions
Suppressing Display
Changing Link Styles
Changing the Number of Links Displayed
Customizing Actions
7.6.4. ColumnRenderer
7.6.5. AuthenticatedContent
7.6.6. ChildContentComponent
7.6.7. UpdatePage and UpdatePageTag
7.6.8. Validator
8. View Engines
8.1. Introduction
8.2. View Engine Comparison
8.3. NVelocity
8.3.1. NVelocity files
8.3.2. Layouts
8.3.3. Configuration
8.3.4. Macros
8.3.5. Fancy foreach Loops
8.3.6. NVelocityViewEngine Variables
8.3.7. Accessing the PropertyBag
8.3.8. ViewComponent Support
Passing Parameters in a Dictionary
Key/Value Pairs
Data Type Handling
A Simple Example
8.4. WebForms
8.4.1. Layouts
8.5. Brail
8.5.1. Getting Started
Referencing Assemblies
Configuration
Using It
8.5.2. Principal of Least Surprise
8.5.3. Configuration
8.5.4. Code Separators
8.5.5. Output Methods
8.5.6. Using Variables
8.5.7. Sub Views
8.5.8. Importing Content From Files
8.5.. Principle of Least Surprise
8.5.10. Common Scripts
8.5.11. Symbols and Dictionaries
8.5.12. Layouts
8.5.13. Performance
Referencing Assemblies
Auto Imports
8.5.14. ViewComponent Support
8.5.15. Troubleshooting
8.5.16. Code Separators
8.5.17. How Brail Works
Processing Requests
Compiling Scripts
BrailBase Class
8.6. Composite View Engine
8.7. Working With Multiple View Engines
9. Filters
9.1. Introduction
9.2. Creating a Filter
9.3. Ordering
9.4. Skipping Filters
9.5. Passing Parameters
10. Layouts
10.1. Introduction
10.2. Using Layouts
11. Rescues
11.1. Introduction
11.2. Using Rescues
12. Authentication and Authorization
12.1. Introduction
12.2. Forms Authentication
12.3. Filters
12.4. Using PrincipalPermission
12.5. The SecurityView Component
13. Helpers
13.1. Introduction
13.2. Creating Customer Helpers
13.3. Built In Helpers
13.3.1. FormHelper
Getting Started
Complex Objects
Arrays
Working with Sets
Generating Selects
Creating Checkbox Lists
13.3.2. UrlHelper
Generating URLs
Generating Links
Generating Button Links
13.3.3. AjaxHelper
Common Parameters
Using It
Using the Behavior Library
Javascript Action Proxies
LinkToFunction and ButtonToFunction
LinkToRemote and ButtonToRemote
Remote Form
Observers
Periodical Updates
Autocompletion
13.3.4. Behavior Helper
13.3.5. ScriptaculousHelper
13.3.6. PaginationHelper
Standard Paging
Cached Paging
13.3.7. WizardHelper
13.3.8. TextHelper
13.3.9. Effects2Helper
13.3.10. DateFormatHelper
14. Resources and Localization
14.1. Introduction
14.2. Using Resources
14.3. Setting Up the Current Culure
14.4. Localization
15. Validation Support
16. Sending Email
17. Unit Testing
17.1. Introduction
17.2. The TestSupport Assembly
17.3. Setting Up a Test Project
17.3.1. A simple example from MonoRail test case
18. Integrations
18.1. Introduction
18.2. ActiveRecord
18.2.1. The Main Players
ARSmartDispatcherController
ARDataBindAttribute and ARDataBinder
ARFetchAttribute
18.2.2. DataBinding with ActiveRecord
DataBinding Issues
Possible Solutions
18.3. ActiveRecord Scaffolding
18.3.1. Required Assemblies
18.3.2. ScaffoldingAttribute
18.3.3. List
18.3.4. Add
18.3.5. Edit
18.3.6. Remove
18.3.7. Complex Models
18.4. Windsor Container
18.4.1. Benefits
18.4.2. Required Assemblies
18.4.3. Configuration
18.4.4. Exposing the Container
18.4.5. Initializing
19. Advanced Topics
19.1. Introduction
19.2. Security
19.3. Routing
19.3.1. Configuration
19.3.2. Root Directory Mapping Workaround
19.3.3. Another Approach
19.3.4. Additional Information
19.4. Caching Support
19.5. Transformation Filters
19.6. Dynamic Actions
19.6.1. Dynamic Action Providers
19.7. Scaffolding
19.8. Extensions
19.8.1. Creating Custom Extensions
19.8.2. Built In Extensions
Custom Session Extension
Exception Chaining Extension
19.9. Service Architecture
19.9.1. How It Works
19.9.2. Lifecycle Interfaces
19.9.3. Registering Services
19.9.4. Built-In Services
MonoRailConfiguration
ExtensionManager
IViewSourceLoader
IViewEngine
IScaffoldingSupport
IControllerFactory
IViewComponentFactory
IFilterFactory
IResourceFactory
IEmailSender
IEmailTemplateService
IControllerDescriptorProvider
IResourceDescriptorProvider
IRescueDescriptorProvider
ILayoutDescriptorProvider
IHelperDescriptorProvider
IFilterDescriptorProvider
IControllerTree
ICacheProvider
19.10. Custom Bindable Parameters
19.11. Using Resources to Store Views
19.12. Enabling Logging

Chapter 1. Introduction

1.1. Overview

MonoRail is a fully MVC compliant Web Framework inspired by Action Pack . MonoRail differs from the standard WebForms approach to development as it enforces separation of concerns; controllers simply handle application flow, models represent the data, and the view is only concerned with the logic surrounding the presentation of that data. Consequently, you write less code and end up with a more maintainable application.

Although the project name is MonoRail, we do not have any affiliation with the Mono project. MonoRail runs on Microsoft .Net 1.1, 2.0 and Mono.

1.2. Background

Before diving into the details of MonoRail it's probably worth understanding a little more about what it is, what value it brings to web development and how it works. Further information on each of these topics is covered in the following sections.

1.2.1. What is MVC

Model-view-controller (MVC) is an architectural pattern used in software engineering. In complex computer applications that present a large amount of data to the user, a developer often wishes to separate the logic concerning data (otherwise known as the model ) and the user interface (or view ) so that changes to the user interface will not affect data handling, and that the data can be reorganized without changing the user interface. The model-view-controller pattern solves this problem by decoupling data access and business logic from data presentation and user interaction through the introduction of an intermediate component: the controller .

Model

The model in the MVC pattern is the domain-specific representation of the information on which the application operates. Domain logic adds meaning to raw data (e.g., calculating if today is the user's birthday, or the totals, taxes, and shipping charges for shopping cart items). Many applications use a persistent storage mechanism (such as a database) to store data. MVC does not specifically mention the data access layer because it is understood to be underneath or encapsulated by the Model.

View

The view renders the model into a form suitable for interaction, typically a user interface element. Multiple views can exist for a single model for different purposes.

Controller

The controller is responsible for processing and responding to events, typically user actions, and may invoke changes on the model.

1.3. Why Use MonoRail

MonoRail differs from the standard WebForms approach to development as it enforces separation of concerns; controllers simply handle application flow, models represent the data, and the view is only concerned with the logic surrounding the presentation of that data. Consequently, you write less code and end up with a more maintainable application.

Although MonoRail can use WebForms as a view engine, it does not work as smoothly as others engines. If your project depends too much on third party Web controls or if you IT team skill set is totally centered around WebForms, MonoRail would not be a good choice. Because MonoRail does not provide abstractions over the page processing like WebForms, there is an additional learning curve for developers that were introduced to web development using pure WebForms without knowledge of the HTTP protocol.

Following are some other benefits of using the MonoRail framework.

1.3.1. Convention Over Configuration

Like RoR, MonoRail prefers convention over configuration. The project structure skeleton is the same throughout all MonoRail enabled solutions. The MonoRail installer also includes a handy templated solution provider for both VS.NET 2003 and 2005 to create the project skeleton and testing is included. Accustomed MonoRail developers can open any solution and just know where things are going to be.

1.3.2. Container support

MonoRail supports IoC via the Windsor container. Controllers and their dependencies/parameters can be injected by the container if necessary.

1.3.3. Testability

Controllers are testable classes sitting outside of the usual ASP.NET cruft. There are a number of framework features within MonoRail which specifically aid testing.

1.4. How It Works

MonoRail is essentially a front controller sitting over the ASP.NET infrastructure intercepting specially formed URI’s. This is achieved through the provision of an alternate implementation of the IHttpHandler interface that extracts information from the Url and searches for a controller in a binary tree. If found, the controller is created and from that point on, the controller handles the request.

The controller is able to select the action (method) to invoke, process the arguments (in the case of SmartDispatcherController ) and delegate the execution to it.

When the action returns, the controller checks if a view was selected (and no redirect was issued). If so the ViewEngine is invoked to process the selected view, which renders the content directly to the client's browser.

As MonoRail runs on top of Asp.Net infrastructure (not to be confused with WebForms) it takes advantage of all features like Session management, Application/Request level events and security.

1.5. Licence Information

MonoRail is release under the terms of Apache Software Foundation License 2.0 . Please refer to the Apache License for a friendly description.

1.6. Support

Castle Stronghold, which sponsors the project and was started by Castle Project's founder can provide commercial support for MonoRail, however the community surrounding MonoRail also provides excellent support through the Castle forums and mailing lists.

Chapter 2. Getting Started

2.1. Introduction

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.

Tip

You can also download the complete example demonstrating the concepts from here.

2.2. Requirements

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.

2.3. Creating the Project Skeleton

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.

2.3.1. Using the MonoRail Project Wizard

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.

Warning

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:

  1. Open Visual Studio and go to NewProject . Under Visual Studio 2005 you will see the following dialog:

  2. Select the Castle MonoRail Project template, enter GettingStartedSample as the name for your new project then click the OK button.

  3. When the wizard starts select the NVelocity View Engine from the list of View Engines and click the Next button.

    Note

    Do not enable Windsor Integration or Routing at this stage.

  4. 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 Finish 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.

2.3.2. Creating the Project Manually

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 the Project

Create an ASP.Net Web Application project in Visual Studio 2005. Name the new project GettingStartedSample then click the OK button.

Tip

If the ASP.Net Web Application template is not present in your Visual Studio installation you can create a Class Library project instead.

Reference the MonoRail Assemblies

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.

AssemblyDescription
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.

Create the Project Folders

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.

Configuration

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.

2.4. 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.

2.4.1. Your First Controller

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.

2.4.2. The Index Action and View

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.

Tip

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>

Tip

You can make Visual Studio use the HTML editor for .vm files. Use the Open With... 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.

2.4.3. Setting the Layout and Rescue

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.

2.4.4. Creating the Layout

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 " / " .

2.4.5. Seeing the Results

If you created the application with the wizard, just hit DebugStart 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.

Note

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 .

2.4.6. Passing Values to a View

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.

2.4.7. Creating a Rescue

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.

2.5. Data Binding

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.

2.5.1. Simple Parameters

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.

2.5.2. Complex Objects

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>

Note

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.

2.6. Integrating with ActiveRecord

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.

Note

For more information on ActiveRecord see the ActiveRecord documentation .

2.6.1. Adding Assemblies

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

2.6.2. Configuration

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.

Note

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.

Warning

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.

2.6.3. Building the Model

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);
		}
	}
}

Note

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 .

2.6.4. Initializing the Handler

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.

Warning

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.

2.6.5. ActiveRecord Scaffolding

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.

Adding Assemblies

First, add references to the assemblies required for ActiveRecord Scaffolding support:

  • Castle.MonoRail.ActiveRecordScaffold.dll

  • Castle.MonoRail.ActiveRecordSupport.dll

Creating the Controller

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.

Viewing the Result

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.

The supplier list

Adding a supplier

2.6.6. Creating CRUD Pages Using DataBind

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.

Presenting the List

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>&nbsp;</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.

Creating

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.

Updating

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.

Deleting

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.

2.7. Final Comments

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.

Chapter 3. Installation

3.1. Introduction

Installation deals with setting up an environment on a server.

3.2. Running Under IIS

If you are running IIS, then you need to associate the the extension you want to use for MonoRail with ASP.Net ISAPI. This is a very simple operation. If you are concerned about shared hosting see the last section on this document.

Usually the extension is rails or castle , but you can use whatever extension you want.

First step: open the IIS MMC

  1. Right-click the Default Web Site item and choose Properties

  2. Select the Home Directory tab

  3. Click Configuration

  1. Cli