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. Click Add.

  2. Select the ISAPI.DLL . You can copy-and-paste the complete DLL file name from another extension, such as .aspx . In most systems it will be something like C:\WINDOWS\Microsoft.NET\Framework\v1.1.4322\aspnet_isapi.dll (for .NET 1.1) or C:\WINDOWS\Microsoft.NET\Framework\v2.0.50727\aspnet_isapi.dll (for .NET 2.0).

  3. Fill in the extension (for example .rails ) as the file extension (make sure you do not omit the leading dot).

  4. Uncheck the Check file exists check box.

Note

Windows XP Pro users : If your OK button is disabled even if you have Administrator rights click in the Executable text field. This will expand the relative path to the dll into a full path and will enable the OK button.

3.3. Using Casini

Cassini is the easiest way to run the application as it requires nothing to be configured. It is suitable for development. You can run it from VS.Net and then be able to debug your application easily.

If you are using Visual Studio 2005, then you might want to use the integrated web server as it is basically the same.

3.4. Mono with XSP

Open a shell and go to directory containing web.config. Call xsp:

xsp --port 80

Notice that Apache2 + mod_mono is much more efficient than XSP. Use XSP only for tests and small applications development.

3.5. Mono with Apache

We assume that you have Apache Httpd, mod_mono and xsp from the Mono Project installed.

3.5.1. Configuration

Add the following lines to your httpd.conf:

LoadModule mono_module modules/mod_mono.so
AddHandler mono .rails .aspx .ashx .asmx .ascx .asax .config .ascx
MonoRunXSP True

Alias /test "/web/test"
AddMonoApplications default "/test:/web/test"
<Location /test>
	SetHandler mono
</Location>

The first line adds the mod_mono module to Apache. The second line sets up mod_mono to handle asp.net and MonoRail file extensions. The third line allows Apache to start and stop the mod_mono_server process. The following group of lines sets up /test to map to your application.

3.5.2. Apache Httpd2

If you build Mod_mono from sources, file mod_mono.conf will be installed at /etc/apache2/mod_mono.conf. To load the module you only need to create a symlink and reload Apache:

ln -s /etc/apache2/mond_mono.conf /etc/apache2/mods-enabled/

Place the configuration above on /etc/apache2/sites-enabled/default and change "Location" for "Directory" if your website lives in filesystem (http://httpd.apache.org/docs/2.2/mod/core.html#location). /etc/apache2/sites-enabled/default:

AddHandler mono .rails .aspx .ashx .asmx .ascx .asax .config .ascx
Alias /example /var/www/example
AddMonoApplications default "/example:/var/www/example"
MonoRunXSP True
<Directory /var/www/example>
	SetHandler mono
</Directory>

Paths could change depending of your distribution

3.5.3. Application Deployment

To deploy, simply copy your webapp's bin and Views directory as well as Global.asax and your web.config files to /web/test and then start Apache.

You can now point your browser at http://yourserver.com/test/index.rails or which ever .rails page you choose.

3.6. Deploying to a Shared Host

On a shared hosting, you may not be able to convince the host to map .rails extentions to the ASP.Net framework, which means that you wouldn't be able to "catch" a request for a rails document and map it to the appropriate controller.

A simple solution for this problem is to just switch to .aspx extention (and change the httpHandler configuration, of course). For example:

<httpHandlers>
	<add verb="*" path="*.ashx" 
	   type="Castle.MonoRail.Framework.MonoRailHttpHandlerFactory, Castle.MonoRail.Framework" />
</httpHandlers>

Chapter 4. Configuration

4.1. Introduction

MonoRail requires a small configuration on your web.config . This document should help you to understand the whole configuration schema in details.

The following is a standard minimal configuration required to get MonoRail working with all defaults. Unfortunatelly the project assembly name must be informed (we cannot infer it).

<?xml version="1.0" encoding="utf-8"?>
<configuration>
    <configSections>
        <section 
			name="monorail" 
			type="Castle.MonoRail.Framework.Configuration.MonoRailSectionHandler, Castle.MonoRail.Framework" />
    </configSections>
    
    <monorail>
        <controllers>
            <assembly>ProjectAssembly</assembly>
        </controllers>
    </monorail>
    
    <system.web>
        <httpHandlers>
            <add 
				verb="*" 
				path="*.rails" 
				type="Castle.MonoRail.Framework.MonoRailHttpHandlerFactory, Castle.MonoRail.Framework" />
        </httpHandlers>
        <httpModules>
            <add 
				name="monorail" 
				type="Castle.MonoRail.Framework.EngineContextModule, Castle.MonoRail.Framework" />
        </httpModules>
    </system.web>
</configuration>

This configuration defaults to use the WebForms view engine and uses the ProjectAssembly as the source of controllers and ViewComponents.

Note

The section monorail must be declared on the configSections .

4.2. Formal Definition

The following exposes all available nodes. It can be used to extend and change the default MonoRail behavior by supplying custom implementation of services.

<monorail 
	useWindsorIntegration="true|false"
	checkClientIsConnected="true|false"
	smtpHost="" smtpPort="" 
	smtpUsername="" smtpPassword="" 
	>
	
	<!-- 
	  Custom Factories should be configured on the services node. 
	  The following is supported to be backward compatible
	-->
	
	<customControllerFactory type="type name that implements IControllerFactory" />
	<customComponentFactory type="type name that implements IComponentFactory" />
	<customFilterFactory type="type name that implements IFilterFactory" />

	<controllers>
		<assembly>AssemblyName1</assembly>
		<assembly>AssemblyName2</assembly>
	</controllers>
	
	<viewcomponents>
		<assembly>AssemblyName1</assembly>
		<assembly>AssemblyName2</assembly>
	</viewcomponents>
	
	<viewEngine 
		viewPathRoot="views" 
		customEngine="ViewEngine.Type.Name, Assembly">
		
		<additionalSources>
			<assembly name="" namespace="" />
			<assembly name="" namespace="" />
		</additionalSources>
		
	</viewEngine>

	<!-- List of services ids:

			Custom
			ControllerFactory
			ViewEngine
			ViewSourceLoader
			ViewComponentFactory
			FilterFactory
			ResourceFactory
			EmailSender
			ControllerDescriptorProvider
			ResourceDescriptorProvider
			RescueDescriptorProvider
			LayoutDescriptorProvider
			HelperDescriptorProvider
			FilterDescriptorProvider
			EmailTemplateService
			ControllerTree
			CacheProvider
			ScaffoldingSupport
			ExecutorFactory
			TransformFilterDescriptorProvider
			TransformationFilterFactory
			ViewEngineManager
			UrlBuilder
			UrlTokenizer
			ServerUtility
			ValidatorRegistry
			AjaxProxyGenerator
	-->
	
	<services>
		<service 
			id="[see list above]" 
			type="Service.Type.Name, Assembly"
			interface="optional" />
	</services>
	
	<extensions>
		<extension type="Extension.Type.Name, Assembly" />
		<extension type="Extension.Type.Name, Assembly" />
	</extensions>

	<routing>
		<rule>
			<pattern>(/blog/posts/)(\d+)/(\d+)/(.)*$</pattern>
			<replace><![CDATA[ /blog/view.rails?year=$2&amp;month=$3 ]]]]></replace>
		</rule>
		<rule>
			<pattern>(/news/)(\d+)/(\d+)/(.)*$</pattern>
			<replace><![CDATA[ /news/view.rails?year=$2&amp;month=$3 ]]]]></replace>
		</rule>
	</routing>
	
</monorail>

4.2.1.  The monorail Node

AttributeDescription
useWindsorIntegration Enables Windsor Container integration
checkClientIsConnected Enables checks for client connection that stops the process if the client has disconnected
smtpHost The smtp host, if there is intention to use MonoRail e-mail features
smtpPort The smtp port, if it is not using the default port
smtpUsername The smtp username (if it requires authentication)
smtpPassword The password (if it requires authentication)

4.2.2.  The controllers Node

The controller node takes one or more assembly nodes. The assembly names are used during initialization as MonoRail will inspect them for controllers to construct a tree.

Note

This node is only used by the default controller factory. It may be ignored by different factories. For example, if Windsor Container integration is enabled, the node will be ignored.

4.2.3.  The viewcomponents Node

The viewcomponents node takes one or more assembly nodes. The assembly names are used during initialization as MonoRail will inspect them for ViewComponents to initialize a dictionary.

Note

This node is only used by the default controller factory. It may be ignored by different factories. For example, if Windsor Container integration is enabled, the node will be ignored.

4.2.4.  The viewEngine Node

The viewEngine node informs MonoRail of the views folder (which may be a relative or an absolute path) and allows the programmer to specify a custom implementation of IViewEngine .

AttributeDescription
viewPathRoot The folder that contains the views
customEngine Full .net type name of a type that implements IViewEngine

The additionalSources Node

Some view engines implementation allow you to use assemblies resources to store views, beside the file system. This is a good approach to reuse controllers among projects.

AttributeDescription
name The assembly name (without extension)
namespace Resources can have a namespace in their names. The namespace must be informed so it can be stripped to allow MonoRail to find the view content as it was in the file system.

4.2.5.  The services Node

MonoRail is composed of a few services. They can be replaced by custom implementations.

AttributeDescription
id The service id used internally by MonoRail
type The implementation type name
interface If the service is defined by an interface contract, it should be specified on this attribute.

4.2.6.  The extensions Node

Extensions can be added. They hook some events exposed by MonoRail to act on them augmenting its functionality.

An extension must implement the IMonoRailExtension interface.

4.2.7.  The routing Node

Routes can be added to define rewrite rules for the urls. This allow nicer urls but can only be used under some conditions.

Chapter 5. Controllers

5.1. Introduction

Controllers are extremely important as they are the intelligent piece of the application that orchestrate the application flow. The Getting started section depicted the basic usage of controllers.

5.2. Naming Convention

The class name is used by default as the controller identification. If your controller's name ends with Controller , it will be stripped from the name. You can also use the ControllerDetails attribute to associate a different name to your controller class.

It is advisable that controller classes follow the Name + Controller suffix convention. When the controller is registered, MonoRail strips the suffix and uses the name as the key. The controller name is used on the URL to access it.

The attribute ControllerDetailsAttribute can be used to force the definition of a name to the controller. For example:

using Castle.MonoRail.Framework;

[ControllerDetails("cust")] 
public class Customer : Controller 
{

}

The controller defined above will be accessible using cust on the url.

5.3. Areas

MonoRail supports the concept of areas, which are logic groups of controllers. All controllers belongs to an area. The default area is an empty (unnamed) one.

You can think of a tree of controllers. Each node is an area, each leaf is a controller.

To define an area, use the ControllerDetailsAttribute attribute:

using Castle.MonoRail.Framework;

[ControllerDetails(Area="admin")] 
public class UsersController : Controller 
{
	public void Index() 
	{ 
	} 
}

This controller now is accessible using the following url path:

/admin/users/index.rails

You can also define more than one level:

using Castle.MonoRail.Framework;

[ControllerDetails(Area="admin/users")] 
public class PasswordMngController : Controller 
{ 
	public void Index() 
	{ 
	}
}

This controller now is accessible using the following url path:

/admin/users/passwordmng/index.rails

5.4. Actions

We refer to action the procedures that can be invoked on your controller. Basically it translates to any public instance method your controller exposes.

Note

If you do not want that a specific method be "invocable", it cannot be public.

5.4.1. Default Action

The DefaultActionAttribute attribute provides a way to associate a default action method that will be called if a matching action method can not be found. One possible use is so that a web designer can add views without the need for a developer to add new action methods. To associate a default action with your controller, use the DefaultActionAttribute attribute. This attribute can only be applied at the class level.

Unless you specify which action should be invoked if none is matched, DefaultAction will be used.

[DefaultAction] 
public class HomeController : Controller
{
	public void Index() 
	{
	}

	public void DefaultAction() 
	{ 
		string template = "notfound";

		if (HasTemplate("home/" + Action)) 
		{ 
			template = Action;
		}

		RenderView(template); 
	} 
}

In the following example, the code specifies the action to be invoked

[
DefaultAction("Foo")] 
public class HomeController : Controller 
{ 
	public void Index() 
	{ 
	}

	public void Foo() 
	{ 
		RenderText(Action + " was not found"); 
	}
}

5.5. Redirecting

The Controller offers a handful of Redirect overloads. Some of them allow you to pass along query string parameters.

The following table list some of the overloads:

NameDescription
RedirectToAction(string action) Redirects to another action in the same controller.
RedirectToAction(String action, params String[] queryStringParameters) Redirects to another action in the same controller specifying query string entries.
RedirectToAction(String action, IDictionary parameters) Redirects to another action in the same controller specifying query string entries.
Redirect(String url) Redirects to the specified URL.
Redirect(String url, IDictionary parameters) Redirects to the specified URL specifying query string entries.
Redirect(String controller, String action) Redirects to another controller and action.
Redirect(String area, String controller, String action) Redirects to another controller and action (within an area).

5.6. Other Useful Properties

Request/Response

PropertyTypeDescription
Context Castle.MonoRail.Framework.IRailsEngineContext Gets the context of this request execution.
Session IDictionary Gets the Session dictionary.
Flash Castle.MonoRail.Framework.Flash Gets a dictionary of volative items. Ideal for showing success and failures messages.
HttpContext System.Web.HttpContext Gets the web context of ASP.NET API.
Request Castle.MonoRail.Framework.IRequest Gets the request object.
Response Castle.MonoRail.Framework.IResponse Gets the response object.
Params NameValueCollection Shortcut to IRequest.Params
Form NameValueCollection Shortcut to IRequest.Form
Query NameValueCollection Shortcut to IRequest.QueryString
IsClientConnected bool Shortcut to IResponse.IsClientConnected

Controller information

PropertyTypeDescription
Name string Gets the controller's name (as MonoRail knows it)
AreaName string Gets the controller's area name.
LayoutName string Gets or set the layout being used.
Action string Gets the name of the action being processed.
SelectedViewName string Gets or sets the view which will be rendered by this action. We encourage you to use RenderView or RenderSharedView instead of setting this property.

Others

PropertyTypeDescription
Logger Castle.Core.Logging.ILogger Logger for the controller (you must enable logging first)
IsPostBack bool Determines if the current Action resulted from an ASP.NET PostBack. As a result, this property is only relavent when using WebForms views. It is placed on the base Controller for convenience only to avoid the need to extend the Controller or provide additional helper classes.

5.7. Data Binding

The SmartDispatcherController extends Controller class adding support for parameter binding. This allow you to bind parameters from form elements to your action arguments. Overloads are also supported. MonoRail will invoke the action that it can supply more parameters.

MonoRail is able to bind simple values and complex objects. Both approaches are described in the sections below.

5.7.1. The SmartDispatchController

5.7.2. Simple Parameter Binding

Note

A sample demonstrating the concepts in the section can be downloaded from !download/monorail/trunk/MonoRail.SimpleBindingSample.zip

Consider the following html form:

<form action="/User/Search.rails" method="post">
	Name: <input type="text" name="name" /> 
	Email: <input type="text" name="email" /> 
	Country: 
	<select name="country"> 
		<option	value="44">England</option> 
		<option	value="55">Brazil</option> 
	</select>
	<input type="submit" value="Search" />
</form>

When this form is submitted, the following entries will be present on the Form dictionary:

  • name

  • email

  • country

The standard way of getting those values on the controller is to use one of the dictionaries:

  • Params : Has query string, form and environment entries

  • Form : Has only form entries (method post)

  • Query : Has only query string entries

Having said that your action code could be the following:

using Castle.MonoRail.Framework;

public class UserController : Controller 
{ 
	public void	Search() 
	{ 
		String name = Form["name"]; 
		String email = Form["email"]; 
		String country = Form["country"];

			// Perform search ... 
	} 
}

Now if you switch to SmartDispatcherController you would be able to use the following simpler code instead:

using Castle.MonoRail.Framework;

public class UserController : SmartDispatcherController
{ 
	public void Search(string name, string email, string country) 
	{ 
		// Perform search ... 
	}
}

The SmartDispatcherController is able to perform conversions (more on that below). In this case if the value is not present (ie. it was not submitted), the argument will assume a default value. However, if the value was submitted, but could not be converted, an exception will be thrown and the action will not be invoked.

Note

Since the RC2 release empty strings are converted to null strings.

DateTime Properties

To bind DateTime fields you can pass a single value or multiple values. Each of them will be a part of the DateTime struct. For example, using a single value:

<form action="SaveValues.rails" method="post">
	<input type="text" name="dob" value="1/1/2000"/> 
</form>

Using multiple values:

<form action="SaveValues.rails" method="post">
	<input type="text" name="dobday" value="16" />
	<input type="text" name="dobmonth" value="7"/> 
	<input type="text" name="dobyear" value="1979" />
	<input type="text"name="dobhour" value="4" /> 
	<input type="text" name="dobminute" value="0" /> 
	<input type="text" name="dobsecond" value="0" />
</form>

Regardless of the form approach, the controller action parameter will be the same:

using Castle.MonoRail.Framework;

public class UserController : SmartDispatcherController 
{ 
	public void SaveValues(DateTime dob) 
	{ 
		... 
	} 
}

Nullable Support

Nullables data types are also supported. They will only be populated if the values are present on the form and in non-empty fields.

Array Support

Arrays are also supported on the controller side. You can use two naming approaches on the form elements to make it work.

The first approach is to repeat the element name. For example:

<form action="SaveValues.rails" method="post">
	<input type="text" name="name" value="1" />
	<input type="text" name="name" value="2" />
	<input type="text" name="name" value="3" />
	<input type="text" name="name" value="4" />
	<input type="text" name="name" value="5" />
</form>

The second approach is to use the indexed value notation. The index value is meaningless to MonoRail, but it must be unique per element name. For example:

<form action="SaveValues.rails" method="post">
	<input type="text" name="name[0]" value="1" />
	<input type="text" name="name[1]" value="2" />
	<input type="text" name="name[2]" value="3" />
	<input type="text" name="name[3]" value="4" />
	<input type="text" name="name[4]" value="5" />
</form>

On the controller side, the parameter will be the same independently of the approach in use. All you need to do is to use an array type:

using Castle.MonoRail.Framework;

public class UserController : SmartDispatcherController 
{ 
	public void SaveValues(string[] name) 
	{
		 ... 
	} 
}

5.7.3. Custom Binding

The DataBind Attribute

Note

A sample demonstrating the concepts in the section can be downloaded from !download/monorail/trunk/MonoRail.SimpleBindingSample.zip

If instead of working with flat values you want to populate an object, this is also possible using the DataBindAttribute .

The DataBindAttribute used the Castle.Component.Binder to instantiate and populate the target type. Simple values, nested objects and arrays are supported. As with simple binding, a name convention must be used on the form elements, so the binder can do its work.

First of all you must use a prefix which is required to avoid name clashing. It is as giving the form elements a name space. The form below uses product as a prefix:

<form method="post" action="create.rails">
	<input type="text" name="product.id" />
	<input type="text" name="product.name" />
	<input type="checkbox" name="product.inStock" id="" value="true" /> 
</form>

On the controller action you must specify the prefix as the argument to the DataBindAttribute :

using Castle.MonoRail.Framework;

public class ProductController : SmartDispatcherController 
{ 
	public void Create([DataBind("product")] Product prod) 
	{
	} 
}

Note

The parameter name (in the case above prod ) is not used by the binder and have no relation with the prefix.

The binding of values happens with writable properties only. Fields are never used. The Product class used on the example above would be:

public class Product 
{ 
	private int id; 
	private String name; 
	private bool inStock;

	public int Id 
	{ 
		get { return id; } 
		set { id = value;}
	}

	public string Name 
	{ 
		get { return name; } 
		set { name = value; } 
	}

	public bool InStock 
	{ 
		get { return inStock; } 
		set { inStock = value; } 
	} 
}

Note

Your class must have a default parameterless constructor.

Nested Objects

Nested objects are supported with no deep limit. Suppose the Product class above included a SupplierInfo :

public class Product 
{ 
	private SupplierInfo supplierInfo;

	// others fields omitted

	public SupplierInfo SupplierInfo 
	{ 
		get { return supplierInfo; } 
		set { supplierInfo = value; } 
	}

	// others properties omitted 
}

The declaration of SupplierInfo follow. Note that it uses different types, including an enumerator.

public enum WeightUnit 
{ 
	Kilos, 
	Pounds 
}

public class SupplierInfo 
{ 
	private String brand;
	private float weight; 
	private WeightUnit weightUnit;
	private int warrantyInMonths;

	public string Brand 
	{ 
		get { return brand; } 
		set { brand = value; } 
	}

	public float Weight 
	{
		get { return weight; } 
		set { weight = value; } 
	}

	public WeightUnit WeightUnit 
	{ 
		get { return weightUnit; } 
		set { weightUnit = value; } 
	}

	public int WarrantyInMonths 
	{ 
		get { return warrantyInMonths; } 
		set { warrantyInMonths = value; } 
	} 
}

When adding elements on the form, all you have to care is to include the property name. For the case above it would be:

<form method="post" action="create.rails">
	<input type="text" name="product.id" />
	<input type="text" name="product.name" />
	<input type="checkbox" name="product.inStock" id="" value="true" />

	<input type="text" name="product.supplierinfo.brand" /> 
	<input type="text" name="product.supplierinfo.Weight" />

	<select name="product.supplierinfo.WeightUnit">
		<option value="Kilos">In Kg</option>
		<option value="Pounds">In Pounds</option> 
	</select>

	<input type="text" name="product.supplierinfo.WarrantyInMonths" />

</form>

The rule is prefixname.propertyname1.propertyname2... . The binder is not case sensitive.

Array Support

There are two situations for array support. First, suppose instead of populating a single Product you would want to populate a sequence of them. This demands two changes in the example we have seen so far.

First the form elements must use the indexed notation discussed earlier:

<form method="post" action="create.rails">
	<input type="text" name="product[0].id" />
	<input type="text" name="product[0].name" />
	<input type="checkbox" name="product[0].inStock" id="" value="true" />

	<input type="text" name="product[1].id" />
	<input type="text" name="product[1].name" />
	<input type="checkbox" name="product[1].inStock" id="" value="true" /> 
</form>

Second, on the controller you must declare the parameter as an array of Product s:

using Castle.MonoRail.Framework;

public class ProductController : SmartDispatcherController 
{ 
	public void Create([DataBind("product")] Product[] prods) 
	{
	} 
}

The rule is prefixname[uniqueindex].propertyname1.propertyname2... . The index must be a number, and the same number identifies the same instance.

Another situation is when one or more properties of the binding target are arrays. This case is also supported and not different from what we have seen.

Being practical, suppose the Product class in the example above included a Category array.

public class Product 
{ 
	private Category[] categories;

	// others fields omitted

	public Category[] Categories 
	{ 
		get { return categories; } 
		set { categories = value; } 
	}

	// others properties omitted 
}

It could also be an array of simple values like String s or int s and the solution would be the same. The declaration of the Category follows:

public class Category 
{
	private String name;

	public string Name 
	{ 
		get { return name; } 
		set { name = value; } 
	} 
}

One more time the solution lies on the element names on the form. The property name must be used in the indexed notation:

<form method="post" action="create.rails">
	<input type="text" name="product.id" />
	<input type="text" name="product.name" />
	<input type="checkbox" name="product.inStock" id="" value="true" />

	<input type="checkbox" name="product.categories[0].name" value="Kitchen"/> 
	<input type="checkbox" name="product.categories[1].name" value="Bedroom"/> 
	<input type="checkbox" name="product.categories[2].name" value="Living-room" /> 
</form>

The rule is this case would be prefixname.propertyname1[uniqueindex].propertyname2... .

Generic Lists

Generic Lists are supported and the behavior is the same of the arrays, expect the property declaration of course.

public class Product 
{ 
	private List<Category> categories;

	// others fields omitted

	public List<Category> Categories 
	{ 
		get { return categories; } 
		set { categories = value; } 
	}

	// others properties omitted 
}

Setting the Binding Source

By default the binder will use the Params collection as source of information to bind data. You can define that it should use the QueryString or Form post data instead. To do that use the From property exposed by the DataBindAttribute .

This is a recommend practice for performance and to prevent people from easily override form parameters. For example:

using Castle.MonoRail.Framework;

public class ProductController : SmartDispatcherController 
{ 
	public void Create([DataBind("product", From=ParamStore.Form)] Product product) 
	{ 
		... 
	} 
}

Defining Accessible Properties

As the DataBindAttribute usually act on domain model classes, you might not want that all properties be "bindable". Suppose you are binding an User class. Sensitive properties might allow overriding the password, roles, access levels or audit information. For these cases you can use Allow and Exclude properties of DataBindAttribute .

The values for these properties are a comma separated list of property names, including the prefix. For example:

public class AccountController : SmartDispatcherController 
{ 
	public void CreateAccount([DataBind("account", Allow="account.Name, account.Email, account.Password")] Account account) 
	{ 
		... 
	} 
}

This indicates that you only want to allow the Name , Email and Password properties to bound with the values from the request. All other properties will be ignored.

The Exclude property is the inverse. It prevents the properties indicated from being used, and allow all others.

There is no depth limit. You should be able to allow or exclude properties in any level of the object graph. For example:

public class AccountController : SmartDispatcherController 
{ 
	public void CreateAccount([DataBind("account", Allow="account.Name, account.Address, account.Address.Street")] Account account) 
	{ 
		... 
	} 
}

Binding Errors

Binding errors might ocur, like invalid dates or problems in data conversion. When using simple binding, an exception will be thrown. When using the DataBindAttribute , however, no exception will be thrown.

To access the error information, use the GetDataBindErrors method:

public class AccountController : SmartDispatcherController 
{ 
	public void CreateAccount([DataBind("account")] Account account)
	{ 
		ErrorList errors = GetDataBindErrors(account);
		... 
	} 
}

The ErrorList implements the ICollection so you can enumerate the problems. You can also check if some specific property could not be converted. For example:

public class AccountController : SmartDispatcherController 
{
	public void CreateAccount([DataBind("account")] Account account) 
	{ 
		ErrorList errors = GetDataBindErrors(account);

		if (errors.Contains("DateOfBirth")) 
		{ 
			Flash["error"]= errors["DateOfBirth"].ToString(); // Or Exception
			RedirectToAction("New", Params); 
		}

		... 
	} 
}

BindObject and BindObjectInstance

You do not need to always use parameters to have an object bound. The methods BindObject and BindObjectInstance , exposed by the SmartDispatcherController , allow you to have the same functionality. The benefit is that not under every case you want to perform the bindings. For example:

public class AccountController : SmartDispatcherController 
{ 
	public void CreateAccount(bool acceptedConditions) 
	{ 
		if(acceptedConditions) 
		{ 
			Account account = (Account)BindObject(ParamStore.Form, typeof(Account),"account");
			... 
		}

		... 
	} 
}

Supported Types

The following types are natively supported by the DataBinder component:

Type nameNote
String Empty fields are converted to null strings
All types where IsPrimitive returns true -
Enum It is converted using the name or value. Flags are also supported
Decimal-
Guid-
DateTime The implementation checks for the key plus day , month , year , hour , minute and second . If none elements is found, it falls back to use DateTime.Parse on the value associated with the key.
Array-
Generic Lists-
HttpPostedFile-
TypeConverter If the type is not within the range above, the converter checks for a TypeConverter associated with it that is able to convert from a string.

FormHelper

The FormHelper was created to act together with the binder. It is able to create form elements with the right names and to obtain the existing value (if possible) saving you from populating the elements manually.

For more on the FormHelper visit the Helpers documentation pages.

5.8. Wizards

You can use wizards to present smaller chunks of information to the user, with more immediate feedback. For example, during a registration process or cart check-out you could save their objects into session at each step, and then persist to the database, at the end, when it is all valid and confirmed, instead of having to either save intermediary objects that are not in an acceptable state, or make one massive form.

MonoRail has built in support to create wizards like chained pages.

Note

A sample demonstrating the concepts in the section can be downloaded from !download/monorail/trunk/MonoRail.WizardSample.zip

Wizard can be created easily on MonoRail, but first you must understand what entities are involved and what role they play.

  • The wizard controller

    A controller must be a wizard parent. You can have as many wizards on a web site as you want. All you need to do is using the WizardActionProvider and implement the interface IWizardController

  • WizardActionProvider

    This is a built in action provider that manages the wizard flow per user. It takes care of starting the wizard, initialize the steps and delegating the execution to the active step

  • Steps

    Each wizard is composed of at least one step. Each step is a controller that extends from WizardStepPage .

Check the flow on the image below:

5.8.1. Wizard Controllers

The wizard controller is just an ordinary controller that might extend Controller or SmartDispatcherController or any other you might have in your controller hierarchy. The only two important things you must do is:

  • Bind the controller to the WizardActionProvider

  • Implement the interface IWizardController

At this point you may be wondering why we have decided to expose wizard support through an interface and a dynamic action provider. The answer lie on the same reason why Dynamic action were created, we should not mess up with your controller hierarchy. If you're just playing with MonoRail for now you might not be able to foresee the problems that might arise if we introduce and force you to extend a specific controller class. In the end of the day we didnt want to copy & paste the use of filters, layouts and resources from our base controller to the wizard controller.

This architectural decision allow you to reuse (vertically) your controller hierarchy, thus not being intrusive to your controller object model.

To bind your controller to an action provider, use the attribute DynamicActionProviderAttribute :

[DynamicActionProvider(typeof(WizardActionProvider))]
public class MyWizardController : Controller, IWizardController 
{ 
	...

This action provider, when executed by the framework, will check whether the controller is implementing the interface IWizardController . So the next logical step is to properly implement it.

To do so, you must at least provide empty bodies for the following IWizardController 's methods:

  • void OnWizardStart()

  • bool OnBeforeStep(string wizardName, string stepName, WizardStepPage step)

    return true if you don't want to block any step from being executed

  • void OnAfterStep(string wizardName, string stepName, WizardStepPage step)

But you must implement the method GetSteps which is the heart of the wizard feature:

public WizardStepPage[] GetSteps(IRailsEngineContext context) 
{ 
	return new WizardStepPage[] { 
		new IntroductionStep(), 
		new MainInfoStep(), 
		new SubscribeStep(), 
		new ConfirmationStep(), 
		new ResultStep() 
	}; 
}

Each step is a class that extends WizardStepPage . The order that you create the array and return it from GetSteps is the order on which the steps will be presented to the user.

5.8.2. Wizard Action Provider

The WizardActionProvider is reponsible for:

  • Make the step's actions available

  • Using the session to store the current wizard step

  • Delegating execution to nested actions

First, when this dynamic action is invoked by MonoRail the action start is added to the wizard controller, so you can point your browse to

http://yourhost/mywizard/start.rails

to start the wizard. This is not required, though but it is a good way to ensure the state is cleaned. If you direct the browser to any specific step, the wizard will start itself correctly if it does not find the proper entries in the session.

5.8.3. Steps

The GetSteps method returns an array of WizardStepPage which you should exted to create your own steps. For example, a very simple wizard step would be

public class IntroductionStep : WizardStepPage 
{ 
}

Not surprisingly this will rely on defaults to work. So when this step is invoked it will just render the view named IntroductionStep (IntroductionStep.vm, or IntroductionStep.aspx or IntroductionStep.boo depending on the view engine you're using) which '''must be on the view folder for the MyWizard controller'''.

However WizardStepPage provide a few lifecycle methods that can be overriden:

MethodDescription
void Initialize(Controller wizardController) This can be overriden but it's important to invoke the base implementation
void Reset() Invoked when the wizard is being access from the start action. Implementors should perform session clean up (if they actually use the session) to avoid stale data on forms
String ActionName { get; } If you want to customize the step name. Defaults to the step's class name
void RenderWizardView() Used to decide on which view to render

And the good news: the WizardStepPage is nothing but a class extending SmartDispatcherController , so you can (and should) create your own methods to perform the step work.

To access a step you should direct your browser to

http://yourhost/mywizard/stepname.rails

Where stepname stands for the value returned by the property ActionName defaulting to the class name. So if you want to access the IntroductionStep we just mentioned, you should use

http://yourhost/mywizard/introductionstep.rails

5.8.4. Nested Actions

What we have seen so far is not enough to create a decent wizard. If you want to use ajax on a step for example, what do to? How to save a form from a step?

To solve these problems nested actions were introduced. Nested actions are handled by the WizardActionProvider and we're basically talking about accessing a step and then an action it holds.

Being a bit more concrete, suppose you have coded the following step:

	public class AccountInfoStep : WizardStepPage { }

And on the view side you present a form gathering information from the user. You can create an action just like you would on any controller:

public class AccountInfoStep : WizardStepPage 
{
	public void Save( ... ) 
	{ 
	} 
}

Now the trick part. To access a nested action you must use

stepname - actioname

So you have to change the form action on the view to

<form action="AccountInfo-Save.rails" method="post"> ...

Please note that the action is just like a regular action, so you must redirect the user at the end or provide a view to be rendered after it's execution.

5.8.5. DoNavigate

Using the same Save example, suppose that you want to direct the user to the next wizard step (if the data is OK). In this case you should invoke the method WizardActionProvider.DoNavigate() :

public class AccountInfoStep : WizardStepPage 
{ 
	public void Save( ... ) 
	{ 
		try 
		{ 
			// validates the data and if it's not ok, throw an exception .. work work work ..

			DoNavigate(); 
		} 
		catch(Exception ex) 
		{ 
			Flash["error"] = ex;
			RedirectToAction(ActionName); 
		} 
	} 
}

DoNavigate is a black box method, but it's easy to understand it. For most of the times you use it, it would be just like you were invoking the method RedirectToNextStep , however you can use a form field named navigate.to to customize where it should go:

  • If navigate.to is previous , DoNavigate will invoke RedirectToPreviousStep

  • If navigate.to is first , DoNavigate will invoke RedirectToFirstStep

  • If navigate.to is empty or not specified, DoNavigate will invoke RedirectToNextStep

  • If navigate.to starts with uri: , DoNavigate will invoke Redirect with the specified URL

  • Otherwise DoNavigate will assume that the specified text on navigate.to is a step name and will invoke InternalRedirectToStep with the specified value

5.8.6. Conditional Steps

You can associate conditions with steps TODO more on this

5.8.7. The WizardHelper

The WizardHelper is automatically added to the wizard controller and the steps. You can use it to create links to previous and next steps and to query whether there's a previous or next step.

TODO More on [[MonoRail:WizardHelper]]

5.8.8. Windsor Integration

If you are using Windsor Integration, then it's up to you to make the steps components or not.

To use the steps as components, register them within the container (configuration file or via code) and code your wizard controller like this:

[
DynamicActionProvider( typeof(WizardActionProvider) )]
public class MyWizardController : Controller, IWizardController 
{ 
	private readonly IKernel kernel;

	public MyWizardController(IKernel kernel) 
	{
		this.kernel = kernel; 
	}

	public WizardStepPage[] GetSteps(IRailsEngineContext context) 
	{ 
		return new WizardStepPage[] {
			(WizardStepPage) kernel[ typeof(IntroductionStep) ],
			(WizardStepPage) kernel[ typeof(MainInfoStep) ],
			(WizardStepPage) kernel[ typeof(SubscribeStep) ],
			(WizardStepPage) kernel[ typeof(ConfirmationStep) ],
			(WizardStepPage) kernel[ typeof(ResultStep) ] 
		}; 
	}
	...

Chapter 6. Views

6.1. Introduction

From the MonoRail perspective the view is in charge of presenting the data supplied by the controller. The controller is not aware of how the view is implemented, nor the underlying view engine you chose. This is intentional and is what separation of concerns is all about.

6.2. Folder Structure Convention

There must be a folder for the views. Each subfolder should be named after the controller's name. If the controller is associated with an area, that must be reflected on the structure as well.

6.3. Selecting a View to Render

When an action is invoked, MonoRail pre-selects the action name as the view to be rendered. For example:

using Castle.MonoRail.Framework;

public class CustomerController : Controller
{
	public void Index()
	{
	}
}

If the action Index is invoked (from a client's browser) the views\customer\index view file will be preselected.

Note

MonoRail will never use file extensions on the controller to define the view to be rendered. Every View Engine uses a different file extension, but that is not reflected on the controller's code.

If instead of sending the views\customer\index the programmer wanted to send a different view, she could use the RenderView method which selects a different view.

using Castle.MonoRail.Framework;

public class CustomerController : Controller
{
	public void Index()
	{
		RenderView("welcome");
	}
}

The code above will select a view file located at views\customer\welcome .

Warning

The view is not processed when RenderView is invoked. It just selects the view. The view process happens after the action method returns.

6.4. Passing Values to a View

6.4.1. The PropertyBag

You would probably want to supply data to the view so it can generate dynamic content. This should be done using the PropertyBag . For example:

using Castle.MonoRail.Framework;

public class TestController : Controller
{
	public void ShowTime()
	{
		PropertyBag["now"] = DateTime.Now;
	}
}

The PropertyBag is an dictionary. Every View Engine uses an approach to make the data available to the view. Using NVelocity View Engine as an example, the data will be present as a context variable. The following is a small NVelocity template example:

<html>
Hello, the time now is $now
</html>

For further examples of how to access the PropertyBag from other view engines see the View Engines section.

6.4.2. Flash

Flash is a way to persist a transient value between requests. It is useful when you perform some process and issue a redirect. On the redirect target you can check the Flash for a status code, error message or something equivalent. For example:

using Castle.MonoRail.Framework;

public class AdminController : Controller
{
	public void PasswordManagement()
	{
	}

	public void ChangePassword()
	{
		String passwd = Params["password"];
	
		if (passwd.Length < 6)
		{
			Flash["error"] = "Password too weak, operation aborted";
		}
		else
		{
			// Change password
		}
	
		RedirectToAction("PasswordManagement");
	}
}

The flow might be not clear in the example above. So, let's see what exactly happens:

  1. The user access the action PasswordManagement .

  2. A page with some action appears, including a change password form that posts to the ChangePassword action.

  3. On the ChangePassword action we perform a naive check and, in the event of failure, add an entry to the Flash.

  4. We send the user back to the PasswordManagement action sending a redirect.

  5. The view for the PasswordManagement needs to check the flash entry error and show a meaningful error message accordingly.

6.5. Shared Views

Some views could be shared among controllers, or you might want to render a view from another controller. For those cases, use RenderSharedView

using Castle.MonoRail.Framework;

public class CustomerController : Controller
{
	public void Index()
	{
		RenderSharedView("common/welcome");
	}
}

The code above will select a view file located at views\common\welcome .

6.6. Cancelling a View

Although it might sound strange, there are situation were you do not want any view processing to take place. For those cases, use the CancelView method.

using Castle.MonoRail.Framework;

public class CustomerController : Controller
{
	public void Index()
	{
		CancelView();
	}
}

6.7. Accessing Values Passed by the Controller

Each view engine accesses the values in the PropertyBag and Flash differently. See the ViewEngines section for further information.

6.8. Javascript and Ajax

We did not reinvent the wheel. We use the awesome prototype js library .

There are other Javascript libraries that can be used with MonoRail:

When passing parameters to an action you need to use the with parameter used by the AjaxUpdater/AjaxRequest . For example in your view you would add:

$AjaxHelper.LinkToRemote("Some action", "ProcessItem.rails", "%{update='resultdiv', with='productid=10'}")

And the Action on the controller that handles the Ajax request:

public void ProcessItem(int productid) { ... }

6.8.1. Javascript Generation

6.8.2. JSON

6.8.3. The JSON Binder

Chapter 7. View Components

7.1. Introduction

It is very common to have a portion of UI content that is reused amongst pages. When the content is more than simple static content you can rely on the ViewComponent infrastructure.

A ViewComponent is a class that resembles the controller functionality. It might use views and send data to the view. It also support inner sections and parameters.

Note

A sample demonstrating the concepts in the section can be downloaded from !download/monorail/trunk/MonoRail.ViewComponentSample.zip

7.2. Creating a View Component

A ViewComponent is a class that extends ViewComponent abstract class. Three methods can be optionally overriden to customize its behavior:

  • Render : selects the view or uses another approach to render the component content

  • Initialize : used to intialize the state of your view component, usually by examining supplied parameters

  • SupportsSection : invoked by the view engine to check if the component supports the section supplied on the view

Note that starting with v1RC3, much of the work normally handled in the Initialize and SupportsSection methods is now done using attributes, so it should be rare to need those methods.

A very minimal ViewComponent could be the following:

using Castle.MonoRail.Framework;

public class HeaderComponent : ViewComponent
{
}

The ViewComponent above will fallback to the default behavior as nothing was customized. The default behavior is to render the view associated with the component, which should lie on the folder components/headercomponent/default . For example, if you were using NVelocity view engine it would be $siteRoot/Views/components/headercomponent/default.vm .

Just like controllers, you can select different views. For example:

using Castle.MonoRail.Framework;

public class HeaderComponent : ViewComponent
{
	public override void Render()
	{
		RenderView("otherview");
	}
}

For the case above the selected view would be components/headercomponent/otherview.vm (in the case of NVelocity view engine).

Similarly, if all the HTML rendering is handled in code, then you can choose to use no view at all:

using Castle.MonoRail.Framework;

public class HeaderComponent : ViewComponent
{
  public override void Render()
  {
     /* .... */
     CancelView();
  }
}

7.3. Using View Components

A ViewComponent has no relation with controller, only with the views selected by the controllers.

When it comes to usage, ViewComponents can be used with a block (nested content) or without (inline). The name of the component (class name) is used to identify the ViewComponent you want to render. Usage varies depending on the View Engine implementation.

#component(HeaderComponent)

ViewComponents that use the nested content usually use a different syntax:

#blockcomponent(NewsComponent)
<ul>
#foreach($new in $news)
  <li>$news.Date $news.Title</li>
#end
</ul>
#end

7.4. Passing Parameters

The programmer can supply parameters to the ViewComponent.

On the view side, parameters are supplied in different ways, depending on your view engine of choice:

#blockcomponent(TableComponent with "elements=$items" "border=0" "style=border: 1px solid black;" "cellpadding=0" "cellspacing=2")
...
#end

In the ViewComponent's code, parameters can be accessed using the property ComponentParams . For example:

using Castle.MonoRail.Framework;

public class TableComponent : ViewComponent
{
	private ICollection elements;
	
	private object border;
	private string style;
	private object cellpadding;
	private object cellspacing;

	public override void Initialize()
	{
		elements = (ICollection) ComponentParams["elements"];
		
		border = ComponentParams["border"];
		style = (String) ComponentParams["style"];
		cellpadding = ComponentParams["cellpadding"];
		cellspacing = ComponentParams["cellspacing"];
		
		base.Initialize();
	}
	
	...

ViewComponentParamAttribute

using Castle.MonoRail.Framework;

public class TableComponent : ViewComponent
{
	[ViewComponentParam("elements", Required=true)]
	public ICollection elements {get; set;}
	
	[ViewComponentParam]
	public object border  {get; set;}
	
	[ViewComponentParam]
	public string style  {get; set;}
	
	[ViewComponentParam]
	public object cellpadding  {get; set;}
	
	[ViewComponentParam]
	public object cellspacing  {get; set;}
	
	public override void Initialize()
	{
	    base.Initialize();
	}

	...

The ViewComponentParamAttribute will automatically bind the parameter to the property. The attribute will only bind to public properties . (Note that the C# v3/VS2008 syntax is used above for the properties. For VS2005 and earlier, the getters and setters would need fully implemented bodies.)

ViewComponentParamAttribute binds the parameter with the given name to the property. If no name is given in the ViewComponentParamAttribute , then it binds the parameter with the same name as the property.

7.5. Block and Nested Sections

The inner content block is the content that is enclosed by a ViewComponent used as a block. For example:

#blockcomponent(RepeatComponent)

This is the inner content
$counter

#end

The ViewComponent has control over the nested content and can renders it how many times it wants to. The following component renders the inner content five times:

using Castle.MonoRail.Framework;

public class RepeatComponent : ViewComponent
{
	public override void Render()
	{
		for(int i=0; i < 5; i++)
		{
			PropertyBag["counter"] = i;
			Context.RenderBody();
		}
	}
}

Often this is not enough to create a reusable ViewComponent. For these cases you can have inner sections with your view component. The ViewComponent's code can have a more elaborated logic to render the section contents. For example:

#blockcomponent(TableComponent with "elements=$items")
#colheaders
<tr>
	<th>&nbsp;</th>
	<th>Element</th>
</tr>
#end

#item
<tr>
	<td>$index</td>
	<td>$item</td>
</tr>
#end

#altitem
<tr>
	<td align="center">$index</td>
	<td>$item</td>
</tr>
#end
#end

In the example above there are three distinct inner sections: colheaders , item and altitem . The TableComponent renders a simple table and is defined in the following code:

using Castle.MonoRail.Framework;

[ViewComponentDetails("Table"), Sections="colheaders,item,altitem")]
public class TableComponent : ViewComponent
{
	[ViewComponentParam("elements", Required=true)]
	public ICollection elements {get; set;}
	
	[ViewComponentParam]
	public object border  {get; set;}
	
	[ViewComponentParam]
	public string style  {get; set;}
	
	[ViewComponentParam]
	public object cellpadding  {get; set;}
	
	[ViewComponentParam]
	public object cellspacing  {get; set;}

	public override void Render()
	{
		RenderText(
			String.Format("<table border=\"{0}\" style=\"{1}\" cellpadding=\"{2}\" cellspacing=\"{3}\">", 
			  	border, style, cellpadding, cellspacing));
	
		if (Context.HasSection("colheaders"))
		{
			Context.RenderSection("colheaders");
		}
		
		if (elements != null)
		{
			int index = 0;
			
			foreach(object item in elements)
			{
				PropertyBag["index"] = ++index;
				PropertyBag["item"] = item;
				
				if (Context.HasSection("altitem") && index % 2 != 0)
				{
					Context.RenderSection("altitem");
				}
				else
				{
					Context.RenderSection("item");
				}
			}
		}
	
		RenderText("</table>");
	}
}

ViewComponentDetailsAttribute provides an name for the component (the default is the name of the class with "ViewComponent" removed), and specified the names of the allowed sections. The list of sections names must be comma separated with no extra spaces, but is not case-sensitive. Any section within the block with a name outside those listed will cause a ViewComponentException to be thrown.

ViewComponentDetailsAttribute is only availabe in v1RC3 and later. When using an eariler version of the framework or for more complex allowed name rules, this can be handled explicitly in code, by implementing the SupportsSection virtual method.

public override bool SupportsSection(string name)
{
    return name == "colheaders" || name == "item" || name == "altitem";
}

The following is a screenshot of three view components in use:

7.6. Built In View Components

MonoRail comes with a few ViewComponents for specific tasks. The documents on this area depicts their usage.

7.6.1. CaptureFor

With the CaptureFor ViewComponent you can define specific data sections on your view and use them on the layout.

Suppose each view can optionally define a javascript block. This block needs to go under the head node on the html page, but it is declared on the layout view. In this case you can use capturefor and define the javascript inside it:

#capturefor(javascript)

  javascript code here

#end

	... rest of the view

The inner content will be available in a variable named $javascript to be used on your layout:

<html>
	<head>
	
	<script type="javascript">
	$!javascript
	</scrit>
	
	</head>

	... rest of the layout view	

7.6.2. SecurityComponent

The SecurityComponent ViewComponent allows you to render the inner content only if the current IPrincipal has the role specified.

Suppose a link can only be seen by users that have the Administrator role:

#blockcomponent(SecurityComponent with "role=Administrator")

  important link here

#end

	... rest of the view

Warning

Do not base your security on hiding links and buttons. Instead combine this with action and resource protection. Please refer to Authentication/Authorization document for more information.

7.6.3. DiggStylePaginationComponent

The DiggStylePagination component allows to create standardized but customizable pagination links for use with MonoRail's PaginationHelper .

You can download the complete sourcecode for the examples below:

Note

A sample demonstrating the concepts in the section can be downloaded from !download/monorail/trunk/MonoRail.DiggStylePagination.zip

Basic Usage

Suppose you have a list of about 200 items to display. Using MonoRail it is easy to create a pagination for that items, such that only 20 at a time are displayed:

public class HomeController : SmartDispatcherController
{
	public void Index()
	{
            PropertyBag["items"] =
                PaginationHelper.CreatePagination<MyEntity>(
                    this, // controller
                    MyEntity.FindAll(new Order("Index", true)), // list
                    10 // number of items per page
                );
	}
}

items is an instance of IPaginatedPage . It can be used like a list in NVelocity templates. Additionally, it has properties to determine the current status of the pagination. These properties can be used to create pagination links, which is described in the PaginationHelper documentation.

However, building pagination links is a repeating task that should be automated. This is what the DiggStylePaginationComponent does.

<table>
	<tr>
		<th>Name</th>
		<th>Index</th>
	</tr>
#foreach($row in $items)
	<tr>
		<td>$row.Name</td>
		<td>$row.Index</td>
	</tr>
#end
	<tr>
    	<td colspan="2">
#component(DiggStylePagination with "page=$items")
		</td>
	</tr>
</table>

This creates a production ready pagination line as shown in the picture below:

Customizing Captions

When creating a non-english application, the labels &laquo; prev and next &raquo; don't make any sense. This can be changed by using a DiggStylePagination blockcomponent and sections to customize the link text:

...data same as above...
<tr>
    <td colspan="2">
#blockcomponent(DiggStylePagination with "page=$items")
    #prev
        &laquo;
    #end
    #next
        &raquo;
    #end
#end
    </td>
</tr>

You can see the results below. Pay attention to encircled links.

Suppressing Display

The problem is that if there are less items than the specified page size, the pagination is still displayed.

This can be suppressed by specifiying the parameter RenderIfOnlyOnePage .

...data same as above...
<tr>
    <td colspan="2">
#component(DiggStylePagination with "page=$items" "renderifonlyonepage=false")
    </td>
</tr>

This removes the pagination itself from the page, but the surrounding table cell is still rendered. In this example this is visible by a thicker bottom border, but depending on the stylesheet this might lead to more ugly effects.

To prevent this, it is possible to render the surrounding HTML by using the sections startblock and endblock . That way the HTML is only rendered when the pagination is needed.

...data same as above...
#blockcomponent(DiggStylePagination with "page=$items" "renderifonlyonepage=false")
#startblock
<tr>
    <td colspan="2">
#end
#endblock
    </td>
</tr>
#end
#end

Changing Link Styles

Rectangular, button-like links are perhaps not your preferred style of displaying the pagination. When you need to change the way links are displayed, you must do so by using CSS classes.

DiggStylePaginationComponent emits the style within the HTML links, so you need to both specify CSS classes and force the component to use these classes.

...data same as above...
<tr>
    <td colspan="2">
#component(DiggStylePagination with "page=$items" "useinlinestyle=false")
    </td>
</tr>

This results in the following HTML rendered:

<div class="pagination">
	<span class="disabled">&laquo; prev</span>
	<span class="current">1</span>
	<a href="/home/index6.rails?page=2">2</a>
	<a href="/home/index6.rails?page=3">3</a>
	<a href="/home/index6.rails?page=4">4</a>
	<a href="/home/index6.rails?page=5">5</a>
	<a href="/home/index6.rails?page=6">6</a>
	&#8230;<a href="/home/index6.rails?page=19">19</a>
	<a href="/home/index6.rails?page=20">20</a>
	<a href="/home/index6.rails?page=2">next &raquo;</a>
</div>

This allows you to customize the output using the following CSS classes:

  • pagination for the complete box containing the pagination.

  • disabled for "buttons" that are not disabled.

  • current for displaying the current page number.

Additionnally, you must specify CSS commands for .pagination a if you want to customize the links themselves.

Here is a comprehensive example that shows all four options:

<style type="text/css" media="screen">
    .pagination {
        /* Complete box */
        background-color: yellow;
        font-size: large;
    }
    
    .pagination a {
        /* links */
        color: red;
    }
    
    .disabled {
        /* disabled "buttons" */
        color: purple;
    }
    
    .current {
        /* current page */
        color: blue;
    }
</style>

This one renders like shown below:

Changing the Number of Links Displayed

If you have a look at the pictures above, you might have noticed that not all pages are linked. By default, only the two pages before and after the current page are linked directly to save space. It is possible to customize the number of links to be displayed on each side of the current page before the ellipsis with the parameter adjacents .

However, the algorithm used doesn't work well with large numbers, so you should use one of the following values:

  • 0 or 1 for very narrow tables

  • 3 for wide tables with a small number of expected pages. This setting suppresses rendering ellipsis for less than ten pages.

  • 2 or nothing (it is the default) for the rest.

Customizing Actions

So far, we have only changed how the links are displayed. This is good enough for simple pages, but if you provide means of searching or filtering the data, you must come up with a possibility to save the parameters for fetching data of the different pages.

Though it is not directly visible to the programmer, any time a different page is displayed, there is a roundtrip to the server using the original controller action. This means that for paging search results, you must pass the search parameters to the controller along with the pagination links.

This can be done using different techniques:

  • Storing the search or filter params in the sessions. Some users are quite fond of this, because they always need the same subset of results. Others, however, are constantly searching for "disappeared" rows, so you should not use session variables only for round-trip-conservation of parameters.

  • Adding the search parameters to the URL of the page links. This is the preferable solution for most simple searches and filters.

  • Calling a JavaScript function from the links. Then use either AJAX for exchanging the rows to be displayed or construct an URL using JavaScript.

Adding Parameters to the URL

The simplest method is specifying the URL to invoke by using the url parameter of the DiggStylePaginationComponent. This allows you to add the needed query params.

If you look at the example of doing this below, you should notice that the URL is assembled in an extra #set -statement. This is intentional; you cannot specify a string as parameter value that contains parameters itself.

<table>
	<tr>
		<th>Name</th>
		<th>Index</th>
	</tr>
#foreach($row in $items)
	<tr>
		<td>$row.Name</td>
		<td>$row.Index</td>
	</tr>
#end
	<tr>
		<td colspan="2">
#set ($url="index8.rails?desc=$desc")
#component(DiggStylePagination with "page=$items" "url=$url")
## The statement below won't work!
##component(DiggStylePagination with "page=$items" "url='index8.rails?desc=$desc'")
		</td>
	</tr>
</table>
<a href="index8.rails">Ascending list</a> &mdash; 
<a href="index8.rails?desc=true">Descending list</a>
Javascript Functions as Link Targets

From the pagination side of view, this is dead simple. Just specify the name of a JavaScript function defined elsewhere in the page and it will be called with the page number as a parameter on every pagination link click.

DiggStylePagination however doesn't help with consuming this information, so it is not recommended unless you are using AJAX nonetheless.

...data same as above...
<tr>
    <td colspan="2">
#component(DiggStylePagination with "page=$items" "paginatefunction=foo")
    </td>
</tr>
<script type="text/javascript">
    function foo(page)
    {
        var a = new AjaxMagicObject();
        a.page = page;
        a.fetchAndDisplayData();
    }
</script>

DiggStylePagination will render this as javascript:foo(page);void(0); , so you really need to specify only the function name.

Building Links the #link

If all else fails, you can provide a block for creating the links. Just be warned, this should really be used only if all else fails; because there is no automation, you need to create all the HTML by yourself.

To build your links, you get the following variables as parameters:

  • $pageIndex contains the page number of the link to render.

  • $url contains the url to the controller action. If it wasn't specified via the url-parameter discussed above, it is simple an URL to the current page.

  • $text contains the text that would have been rendered by the default render functions. This is either a number, prev, next or a custom text specified using #prev or #next .

To get a working pagination, you must not forget to pass a parameter named page to the controller action.

Using #link has the caveat, that it is not called for

  • rendering the current page

  • rendering ellipsis

  • rendering disabled links

The following examples renders all links as buttons. The (imperfect) result is shown below.

<tr>
    <td colspan="2">
#blockcomponent(DiggStylePagination with "page=$items")
#link
<button type="button" onclick="location.href='$url?page=$pageIndex';">$text</button>
#end
#end
    </td>
</tr>

7.6.4. ColumnRenderer

7.6.5. AuthenticatedContent

7.6.6. ChildContentComponent

7.6.7. UpdatePage and UpdatePageTag

7.6.8. Validator

The Validator component is used to validate your objects. It uses an attribute driven syntax making it easy to start adding validation to your classes.

Here is a sample domain object

namespace GettingStartedSample.DomainObjects
{
	using System;

	using Castle.Components.Validator;

	public class Person
	{
		private String _name;
		public String Name 
		{
			get { return _name; }
			set { _name = value; }
		}
	}
}

Here is a simple test to show its use.

namespace GettingStartedSample.DomainObjects.Tests
{
	using System;

	using NUnit.Framework;
	using Castle.Components.Validator;

	[TestFixture]
	public class PersonTests
	{
		[Test]
		public void Should_be_invalid_if_name_is_empty()
		{
			ValidatorRunner runner = new ValidatorRunner(new CachedValidationRegistry());
			Person p = new Person()
			Assert.IsNull(p.Name);
			Assert.IsFalse(runner.IsValid(p));
		}
	}
}

Chapter 8. View Engines

8.1. Introduction

View engines are responsible for rendering the contents back to the browser. You can create your view engine by simply implementing the interface IViewEngine . For example, you can create an XML/XSL view engine, or WML, or whatever you can think of. Notice, however, that the view should be responsible for displaying logic and nothing more, nothing less.

The view engine implementation is also supposed to invoke a few hooks on the controller instance, namely PreSendView and PostSendView .

Below is the declaration of the IViewEngine interface.

/// <summary>
/// Depicts the contract used by the engine
/// to process views, in an independent manner.
/// </summary>
public interface IViewEngine
{
	/// <summary>
	/// Evaluates whether the specified template exists.
	/// </summary>
	/// <returns><c>true</c> if it exists</returns>
	bool HasTemplate(String templateName);

	/// <summary>
	/// Processes the view - using the templateName 
	/// to obtain the correct template,
	/// and using the context to output the result.
	/// </summary>
	void Process(IRailsEngineContext context, Controller controller, String templateName);

	///<summary>
	/// Processes the view - using the templateName 
	/// to obtain the correct template
	/// and writes the results to the System.TextWriter. 
	/// No layout is applied!
	/// </summary>
	void Process(TextWriter output, IRailsEngineContext context, Controller controller, String templateName);

	/// <summary>
	/// Wraps the specified content in the layout using 
	/// the context to output the result.
	/// </summary>
	void ProcessContents(IRailsEngineContext context, Controller controller, String contents);
}

By default the view engines will return pages with the MIME type text/html . If you would like to use application/xml+xhtml you can set this in your web.config file:

<viewEngine 
  viewPathRoot="views"
  xhtmlRendering="true"
  customEngine="Castle.MonoRail.Framework.Views.NVelocity.NVelocityViewEngine, 
Castle.MonoRail.Framework.Views.NVelocity" />

If this attribute is set to true , and the user agent says it will accept application/xml+xhtml then the page will be returned using that MIME type instead. For browsers such as IE which do not understand the new MIME type, text/html will still be used.

8.2. View Engine Comparison

EngineLanguageCompiledHelpersViewComponents
WebFormsAny .net languageYesYesNo
NVelocityVelocityNoYesYes
BrailbooYesYesYes

8.3. NVelocity

Pros:

  • Limited set of functions forces you to code only view logic (good for separation of concerns)

  • Easy to learn template language

  • Same syntax as Velocity (for Java), allowing view reuse among different platforms

  • Reuse skills for people with Java experience

Cons:

  • Interpreted

  • Community seem very inactive. That forced Castle Project to fork NVelocity and work on improvements and bug fixes.

The NVelocity View Engine uses NVelocity

NVelocity is a port of the excellent Apache Jakarta Velocity project. It is a very simple, easy to learn and extensible template engine. Due to the lack of releases, support and bug fixes on the original port, the Castle Team decided to fork the project, bringing it to our code repository, fixing the bugs and improving it with more features.

The first thing you need to read about NVelocity is not even on this web site. You can find on the original Velocity web site.

Frequently Asked Questions on NVelocity View Engine can be found on the standard MonoRail FAQ .

To use NVelocity View Engine inform the type on the customEngine on the configuration file:

<viewEngine 
	viewPathRoot="views" 
	customEngine="Castle.MonoRail.Framework.Views.NVelocity.NVelocityViewEngine, Castle.MonoRail.Framework.Views.NVelocity" />

8.3.1. NVelocity files

NVelocity uses the extension .vm so just create your views with that extension. Remember that from your controller you should not reference file extensions when defining views to render.

8.3.2. Layouts

Use $childContent context variable to render the content of the view on the layout template. The following is a simple layout using NVelocity:

<html>

Welcome

$childContent

Footer

</html>

Note

The view template selected by the controller is executed before the layout template. In fact the layout template is merged with the result of the view template execution.

8.3.3. Configuration

The NVelocity View Engine looks for a file nvelocity.properties in the root of the view folder. You can use this file to configure how NVelocity should behave.

For example, to configure NVelocity to support Chinese encoding create a text file named nvelocity.properties , save it to your views folder and add the following content:

input.encoding=GB2312
output.encoding=GB2312

More information on the entries can be found on the original Velocity documentation.

8.3.4. Macros

The NVelocity supports macros, but keep in mind that they have problems. If you want to use macros you can create a folder macros under the your views root folder.

All .vm files in this folder will be loaded as a NVelocity Macro library so the macros will be available to all templates.

8.3.5.  Fancy foreach Loops

Inspired on http://www.fogcreek.com/CityDesk/2.0/help/Scripting_With_CityScript/FancyLoops.html. The following code should be self-explanatory:

#foreach($i in $items)
#each (this is optional since it's the default section)
       text which appears for each item
#before
       text which appears before each item
#after
       text which appears after each item
#between
       text which appears between each two items
#odd
       text which appears for every other item, including the first
#even
       text which appears for every other item, starting with the second
#nodata
       Content rendered if $items evaluated to null or empty
#beforeall
       text which appears before the loop, only if there are items
       matching condition
#afterall
       text which appears after the loop, only of there are items
       matching condition
#end

All sections are optional, and they can appear in any order multiple times (sections with same name will have their content appended)

So for example you can use it to create a table contents with alternating styles:

#foreach($person in $people)
#beforeall
       <table>
               <tr>
               <th>Name</th>
               <th>Age</th>
               </tr>
#before
       <tr
#odd
       Style='color:gray'>
#even
       Style='color:white'>

#each
       <td>$person.Name</td>
       <td>$person.Age</td>

#after
       </tr>

#between
       <tr><td colspan='2'>$person.bio</td></tr>

#afterall
       </table>

#nodata
       Sorry No Person Found
#end

Which will output something like:

<table>
       <tr>
       <th>Name</th>
       <th>Age</th>
       </tr>
       <tr style='color:white'>
               <td>John</td>
               <td>32</td>
       </tr>
       <tr><td colspan='2'>Monorail programmer</td></tr>
       <tr style='color:gray'>
               <td>Jin</td>
               <td>12</td>
       </tr>
       <tr><td colspan='2'>Castle guru</td></tr>
</table>

If the $people variable was null the output will be:

Sorry No Person Found

8.3.6. NVelocityViewEngine Variables

The NVelocityViewEngine is responsible for making "useful" variables available to your view. Here's the list of variables added to the context by the NVelocityViewEngine:

Context VariableDescription
$controllerThe controller being executed.
$contextThe IRailsEngineContext.
$requestcontext.Request
$responsecontext.Response
$sessioncontext.Session
$childContent Used inside Layouts. It defines the content rendered by a View.
$page Available in *.njs views and is added in the GenerateJS method.
$siterootcontext.ApplicationPath

Additionally - the contents of the following collections are merged into the context.

  • controller.Resources

  • context.Params

  • controller.Helpers

  • context.Flash

  • controller.PropertyBag

Each key in each of the collections becomes a $variable. For example:

class MyController 
{
	public void Index() 
	{
	   PropertyBag["myvariable"] = "some value";
	   Context.Params["othervariable"] = "some other value value";
	   Context.Flash["anothervariable"] = "yet one more";
	}
}

In your view you will have the following variables:

$myvariable
$othervariable
$anothervariable

Helpers are also added to allow you to invoke static members on some common types:

  • $Byte

  • $SByte

  • $Int16

  • $Int32

  • $Int64

  • $UInt16

  • $UInt32

  • $UInt64

  • $Single

  • $Double

  • $Boolean

  • $Char

  • $Decimal

  • $String

  • $Guid

  • $DateTime

This allows you to do useful things like:

The Current time is: $DateTime.Now

8.3.7. Accessing the PropertyBag

If you want to list the variables in the property bag - then add a reference to the PropertyBag to the PropertyBag.

class MyController 
{
	public void Index() 
	{
		PropertyBag["PropertyBag"] = PropertyBag;
	}
}

then in your view $PropertyBag is what you want:

Property bag variables
#foreach($key in $PropertyBag)
#beforeall

	Name
	value
		
#each

	$key
	$PropertyBag.get_Item($key)
			
#afterall
	
#end

8.3.8. ViewComponent Support

NVelocity allows you to create your own directives, so that's how we introduced components to it. Basically you can use:

for inline components

#component(ComponentName)

for components with body content (aka block components)

#blockcomponent(ComponentName)
  some content
#end

ViewComponents have access to the IRailsContext so you can access form parameters, session, etc. Sometimes however it's important to specify some parameters.

Passing Parameters in a Dictionary

Use the name of the component followed by a dictionary string. For more information on dictionary expressions see [[MonoRail:NVelocity]]

#component(MyFirstComponent "%{firstParam='some value',anotherParam='other value'}")

You can then access the parameters from the component code:

public class MyFirstComponent: ViewComponent
{
	public override void Render()
	{
		object param1 = Context.ComponentParameters["firstParam"];
		object param2 = Context.ComponentParameters["anotherParam"];

		...
	}
}

Key/Value Pairs

In this case you need to use the keyword with followed by a sequence of key/value pairs:

#component(ComponentName with "name=john" "address=some address")

You're free to use interpolation as well

#component(ComponentName with "name=${customer.name}")

You can gain access to the component parameters using the Context.ComponentParameters too.

Data Type Handling

Every data type is supported. However literal values will be automatically converted to text. If you want to specify a different type, create a varible on NVelocity or use some structure data available on the view.

The parameter value below will be converted to string

#component(ComponentName with "age=1")

The parameter value below will remain an Int32

#set($age = 27)
#component(ComponentName with "age=${age}")

A Simple Example

The view snippet:

#blockcomponent(SecurityComponent with "role=admin")
  this will only be rendered if the current user is in the specified role
#end

The component code:

using Castle.MonoRail.Framework;

namespace WebApp
{
	public class SecurityComponent : ViewComponent
	{
		bool shouldRender;
	
		public override void Initialize()
		{
			String role = ComponentParameter["role"] as String;
			shouldRender = RailsContext.User.IsInRole( role );
 		}

		public override void Render()
		{
			if (shouldRender) Context.RenderBody();
 		}
	}
}

8.4. WebForms

Pros:

  • Familiar .NET Syntax

  • Set of useful Web Controls

Cons:

  • Does not implement or support MVC logic, so it's easy to implement more logic than view logic on the web form, leading to scattered logic

  • Limitations apply, see the WebForms View Engine documentation

  • Although there is an open-source implementation of it, it is not patent-free and might be discontinued or become unsupported/incompatible in the long term.

With WebForms you can use all your existing skills to develop MonoRail applications, however its integration with MonoRail can be quite tricky. The reason for this is that in MonoRail (or any other MVC framework) the controller should be the first entity invoked. For WebForms, the page is everything (the controller, the view and sometimes the model).

Let's take a look at an example of perfectly decent controller code and explain when it does not integrate with WebForms.

public class AccountController : SmartDispatcherController
{
	public void Index()
	{
		// empty, just render the index.aspx view so the user 
		// can fill the form
	}

	public void Save(String name, String email)
	{
		if (name.Length == 0 || email.Length == 0)
		{
			Flash["error"] = "Please, fill both Name and Email";
			RenderView("index");
			return;
		}
		
		AccountServices.Create(name, email);
		
		RenderView("success");
	}
}

And here is the source code of the page:

<form runat="server">
  Name: <asp:TextBox ID="name" Runat="server" />
  Email: <asp:TextBox ID="email" Runat="server" />
  <asp:Button ID="Save" Text="Save" Runat="server" />
</form>

When the Webform is rendered, the form action will point to the original url, which is account/index.rails . But we want to execute the Save action instead... what to do?

Well, one approach is to handle the Save click on the server side:

<form runat="server">
  <asp:Button ID="Save" Text="Save" Runat="server" OnClick="OnSave" />
</form>

In the WebForm code, you can implement the IControllerAware interface so you have access to the controller instance. This means that in the OnSave event handler, we can delegate the execution back to the controller:

public class Index : System.Web.UI.Page, IControllerAware
{
    private Controller _controller;

    public void SetController(Controller controller)
    {
        _controller = controller;
    }

    public void OnSave(object sender, EventArgs args)
    {
        _controller.Send("Save");
    }
}

While this works, if the Save action is completed successfully it will send back the success.aspx page. The problem here is that WebForm will try to populate the controls tree with the view state on the request, which will fail.

''What if we just change the form action through javascript'' you might inquire. Well, the same view state problem will happen. However, we can substitute the RenderView with Redirect and then everything works. But of course, there's a cost involved.

In this same sample, if the Save action decided that there was missing data and used RenderView("index") then you're in big trouble. That's because Index.aspx will be reprocessed, which it will interpret as a post back and so will invoke the button event handler again, which will delegate to the controller again, and so on.

So, many simple scenarios can get really hard with WebForms.

8.4.1. Layouts

To use MonoRail layouts with the WebForms View engine, you must create an ordinary aspx file on the layouts folder. You must use MasterPageBase as the base class, otherwise you will have a view state name clash.

The following is an example of a layout:

<%@ Page Inherits="Castle.MonoRail.Framework.Views.Aspx.MasterPageBase" %>
<%@ Register tagprefix="rails" namespace="Castle.MonoRail.Framework.Views.Aspx" assembly="Castle.MonoRail.Framework" %>
Different master page
<p><rails:Contents id="contents" runat="server" /></p>
Footer

The control Contents outputs the the view content.

8.5. Brail

Pros:

  • Use the wrist friendly and feature-rich Boo language for templates.

  • Compiled (good performance!)

Cons:

  • Requires additional assemblies

  • Python syntax (which one can consider as a pro)

Brail is a view engine for MonoRail which allows you to use the same framework and Boo language on both ends of the application. You write the business logic using Boo (or any other .Net language), and then you write the views in Boo. No need for a mental switch or learn another templating language.

8.5.1. Getting Started

Brail includes many changes to the way that you normally write code in Boo. Brail views are scripts files that ends with "*.brail" or "*.brailjs". The most significant change is that Brail does not use whitespace to control blocks. This mean that the following statement in Boo:

if someCondition:
   DoAction()

Will look like this in Brail:

if someCondition:
   DoAction()
end

Blocks are controlled starting with a colon and ending by "end". While this is probably the most significant change from Boo, there are many other things that Brail does for you so you would get a scripting experience while working in a compiled langauge. Read on and discover what makes Brail so special.

Referencing Assemblies

First of all you must reference the following assemblies. Copy them to the bin folder if you are not using Visual Studio.

  • Castle.MonoRail.Views.Brail.dll

  • Boo.Lang.dll

  • Boo.Lang.Compiler.dll

  • Boo.Lang.Parser.dll

  • anrControls.Markdown.dll

The Boo.* files are required for the language support. The anrControl.Markdown.dll is required for support Markdown formatting.

Configuration

Edit your web.config file to include the following line (in the monoRail config section, of course):

<viewEngine 
	viewPathRoot="Views" 
	customEngine="Castle.MonoRail.Views.Brail.BooViewEngine, Castle.MonoRail.Views.Brail" />

And that is it, you are now capable of using Boo scripts to write views in MonoRail! Now that you can use it, let's see what you can do with it.

Before we get to how to use it, I must send huge thanks to the Boo Community, for being so helpful.

If your view directory is in the web directory, it's wise to not allow the files to be read through http. So under the system.web/httpHandlers configuration you should add the following:

<add verb="*" path="*.brail" type="System.Web.HttpForbiddenHandler"/>
<add verb="*" path="*.brailjs" type="System.Web.HttpForbiddenHandler"/>

Using It

First, Brail scripts are not normal Boo programs, they ''require'' that you would have at least one statement at the global namespace, which is what MonoRail will end up calling when your view is invoked. Assuming that you've already have a controller, and that you've setup your environment correctly, you can simply do this:

Hello, World!

Brail will take this script, compile it and send its output to your browser. The nice thing about it is that if you would change it to say:

Hi, World!

You will instantly get a the updated view, this is highly important to development scenario.

Note

This is a developer feature, meant to ease development. Using it on a production machine would cause recompiling of the scripts and cause an ''assembly leak'' until the application domain is restarted. On a developer machine, this should rarely be a problem, on a production machine, if frequent changes are made to the scripts, this can cause problems.

You can also use parameters that the controller has put in the Property Bag or Flash, like this:

Hi, ${User}!

If you are wondering how does it work behind the scenes: the script is loaded and compiled. There is some magic there that sends anything not wrapped in <% %> directly to the user (which will also expand ${arg} expression to their values). The compiled code is cached, so you pay the compilation cost only once.

<html>	
	<head>		
		<title>${title}</title>
	</head>	
	<body>	    
	     <p>The following items are in the list:</p>  
	     <ul><%for element in list:	output "<li>${element}</li>"%></ul>
	     <p>I hope that you would like Brail</p>	
	</body>
</html>

The output of this program (assuming list is (1,2,3) and title is "Demo" ) would be:

<html>   
	<head>        
		<title>Demo</title>   
	</head>    
	<body>        
     <p>The following items are in the list:</p> 
     <ul><li>1</li><li>2</li><li>3</li></ul>
	</body>
</html>

And the rendered HTML will look like this:

----
The following items are in the list:	
* 1
* 2
* 3
-----

8.5.2. Principal of Least Surprise

On general, since NVelocity is the older view engine for now, I have tried to copy as much behavior as possible from NVelocityViewEngine. If you've a question about how Brail works, you can usually rely on the NVelocity behavior. If you find something different, that is probably a bug, so tell us about it.

8.5.3. Configuration

The default configuration should suffice for most cases, but if you want to change the configuration, you can do so by adding a configuration section handler to the web.config file:

<configSections>
  <section name="Brail" type="Brail.BrailConfigurationSection, Brail" />
</configSections>

Here is the default configuration for Brail:

<Brail
	debug="false"
	saveToDisk="false"
	saveDirectory="Brail_Generated_Code"
	batch="true"
	commonScriptsDirectory="CommonScripts">

	<reference assembly="My.Assembly.Name"/>
	<import  namespace="My.Assembly.Name"/>
</Brail>
Option: Default Value:Possible values:
debugGenerate debug or retail codefalse

true - generate debug code

false - generate retial code

saveToDisk Save the generated assemblies to disk - useful if you want to know what is going on behind the scenes. false

true - save assemblies to disk

false - use entirely in memory

saveDirectory

The directory to save the generated assemblies to.

The path can be relative or absolute, if relative, the default ASP.Net bin path will be used.

If the directory does not exist, it will be created.

"Brail_Generated_Code"Any valid path
batch

Batch compilation, compile all the scripts in one folder to a single assembly.

This does not work recursively.

true

true - all scripts in a folder will be compiled to a single assembly

false - each script will be compiled to its own assembly

commonScriptsDirectory

The directory where all the common scripts are. This can be a relative or absolute path, if relative, the Views directory of the application will be used as the base.

If the directory does not exist, it will not be created.

"CommonScripts"Any valid path
reference element, assembly attribute This tells Brails that all your scripts should reference the specified assembly or assemblies. This allows strong typing in the views and avoids the cost of reflection. none The assembly attribute must contain a valid assembly name that is reachable to the application by using Assembly.Load() . Usually this means that it's located in the bin directory of the application.
import element, namespace attribute This tells Brails that all your scripts should import the specified namespace or namespaces. This allows shorter naming in the script. noneAny valid namespace

8.5.4. Code Separators

Brail supports two code separators <% %> and <?brail ?>, I find that <% %> is usually easier to type, but <?brail ?> allows you to have valid XML in the views, which is important for some use cases. Anything outside a <?brail ?> or <% %> is sent to the output. ${user.Id} can be used for string interpolation.

Warning

The code separator types cannot be mixed. Only one type of separators must be used per file.

8.5.5. Output Methods

Since most of the time you will want to slice and dice text to serve the client, you need some special tools to aid you in doing this. Output methods* are methods that are decorated by [Html] / [Raw] / [MarkDown] attributes. An output method return value is transformed according to the specified attribute that has been set on it, for instance, consider the [Html] attribute:

<%
[Html]
def HtmlMethod():
	return "Some text that will be <html> encoded"
end
%>
${HtmlMethod()}

The output of the above script would be:

Some text that will be <html> encoded

The output of a method with [Raw] attribute is the same as it would've without it (it's supplied as a NullObject for the common case) but the output of the MarkDown attribute is pretty interesting. Here is the code:

<%
[MarkDown]
def MarkDownOutput():
	return "[Ayende Rahien](http://www.ayende.com/), __Rahien__."
end
%>
${MarkDownOutput()}

And here is the output:

<p><a href="http://www.ayende.com/">Ayende Rahien</a>, <strong>Rahien</strong>.</p>

Markdown is very interesting and I suggest you read about its usage.

8.5.6. Using Variables

A controller can send the view variables, and the Boo script can reference them very easily:

My name is ${name}
<ul>
<%
for element in list:
    output "<li>${element}</li>"
end
%>
</ul>

Brail has all the normal control statements of Boo , which allows for very easy way to handle such things as:

<% output AdminMenu(user) if user.IsAdministrator %>

This will send the menu to the user only if he is administrator.

One thing to note about this is that we are taking the variable name and trying to find a matching variable in the property bag that the controller has passed. If the variable does not exist, this will cause an error, so pay attention to that. You can test that a variable exists by calling the IsDefined() method.

<%
if IsDefined("error"):
	output error
end
%>

Or, using the much clearer syntax of "?variable" name:

<%
output ?error
%>

The syntax of "?variable" name will return an IgnoreNull proxy, which can be safely used for null propagation, like this:

<%
# will output an empty string, and not throw a null reference exception
output ?error.Notes.Count
%>

This feature can make it easier to work with optional parameters, and possible null properties. Do note that it will work only if you get the parameter from the property bag using the "?variableName" syntax. You can also use this using string interpolation, like this:

Simple string interpolation: ${?error}
And a more complex example: ${?error.Notes.Count}

In both cases, if the error variable does not exists, nothing will be written to the output.

8.5.7. Sub Views

There are many reasons that you may want to use a sub view in your views and there are several ways to do that in Brail. The first one is to simply use the common functionality. This gives a good solution in most cases (see below for a more detailed discussion of common scripts).

The other ways is to use a true sub view, in Brail, you do that using the OutputSubView() method:

Some html
<?brail OutputSubView("/home/menu")?>
<br/>some more html

You need to pay attention to two things here:

The rules for finding the sub view are as followed:

  • If the sub view start with a '/' : then the sub view is found using the same algorithm you use for RenderView()

  • If the sub view doesn't start with a '/' : the sub view is searched starting from the ''current script'' directory.

A sub view inherit all the properties from its parent view, so you have access to anything that you want there.

You can also call a sub view with parameters, like you would call a function, you do it like this:

<?brail OutputSubView("/home/menu", { "var": value, "second_var": another_value } ) ?>

Pay attention to the brackets, what we have here is a dictionary that is passed to the /home/menu view. From the sub view itself, you can just access the variables normally. This variables, however, are not inherited from views to sub views.

8.5.8. Importing Content From Files

Occasionally a need will arise to include a file "as-is" in the output, this may be a script file, or a common html code, and the point is not to interpret it, but simply send it to the user. In order to do that, you simply need to do this:

${System.IO.File.OpenText("some-file.js").ReadToEnd()}

Of course, this is quite a bit to write, so you can just put an import at the top of the file and then call the method without the namespace:

<%
import System.IO
%>
${File.OpenText("some-file.js").ReadToEnd()}

8.5.. Principle of Least Surprise

On general, since NVelocity is the older view engine for now, I have tried to copy as much behavior as possible from NVelocityViewEngine. If you've a question about how Brail works, you can usually rely on the NVelocity behavior. If you find something different, that is probably a bug, so tell us about it.

8.5.10. Common Scripts

In many cases, you'll have common functionality that you'll want to share among all views. Just drop the file in the CommonScripts directory - (most often, this means that you will drop your common functionality to Views\CommonScripts) - and they will be accessible to any script under the site.

Note

The language used to write the common scripts is the white space agnostic deriative of Boo, and not the normal one. This is done so you wouldn't have white spacing sensitivity in one place and not in the other.

Note

The common scripts are normal Boo scripts and get none of the special treatment that the view scripts gets. An error in compiling one of the common scripts would cause the entire application to stop.

Here is an example of a script that sits on the CommonScripts and how to access it from a view:

Views\CommonScripts\SayHello.boo - The common script

def SayHello(name as string):
	return "Hello, ${name}"
end

Views\Home\HelloFromCommon.boo - The view using the common functionality

<%
output SayHello("Ayende")
%>

The output from the script:

Hello, Ayende

8.5.11. Symbols and Dictionaries

Quite often, you need to pass a string to a method, and it can get quite cumbersome to understand when you have several such parameters. Brail support the notion of symbols, which allows to use an identifier when you need to pass a string. A symbol is recognized by a preceding '@' character, so you can use this syntax:

<%
output SayHello( @Ayende )
%>

And it will work exactly as if you called SayHello( "Ayende" ). The difference is more noticable when you take into account methods that take components or dictionary parameters, such as this example:

<%
component Grid, {@source: users, @columns: [@Id, @Name] }
%>

Using a symbol allows a much nicer syntax than using the string alternative:

<%
component Grid, {"source: users, "columns": ["Id", "Name"] }
%>

8.5.12. Layouts

Using layouts is very easy, it is just a normal script that outputs ChildOutput property somewhere, here is an example:

Header
${ChildOutput}
Footer

8.5.13. Performance

If you want to use Brail for 10 million transactions a day, I would suggest measuring first, but in general, it should be good for most of what you throw at it.

Batch compilation should reduce compile time and memory size. The code is not interpreted, it's statically compiled (very similar to how ASP.Net does it) and run whenever a request comes in. Currently there is no further reason to complicate the code until someone actually needs it. The second time that a request comes in for a page, it's already compiled and can immediately serve the request.

A change in a single file will cause a separate assembly to be loaded, and all future requests will go the the new assembly immediately. Be aware that a large number of changes in an application will cause an assembly leak, since the assemblies cannot be unloaded until the AppDomain is unloaded. This isn't a problem in production scenarios, and on a development machine, the usual IIS application resets should take care of it.

If you think that reflection kills your performance, make sure to reference your relevant assemblies and use casting to the appropriate types when applicable. Another option would be to improve dynamic dispatch, but that would wait until there is a true need for it.

Referencing Assemblies

Consider the following this code:

<%
for user in users:
	output "<p>${user.Name} - ${user.Email}</p>"
end
%>

Looks simple, right? The problem is that Brail doesn't really know what user is, so it uses reflection to get the values of the Name and Email properties. This is, of course, quite expensive in performance terms. What is the solution? You need to tell Brail to add a reference to the assembly where User is defined. You can do that by adding this line to your web.config file (see the full configuration section below for more details).

<Brail>
     <reference assembly="assembly.with.user.object" />
</Brail>

And then, in your view code, you write:

<%
import My.Models
%>
<!-- lots of html code ->
<%
for user as User in users:
	output "<p>${user.Name} - ${user.Email}</p>"
end
%>

You can also use this out side of loops, in order to get strong typing (and the assoicated performance benefits):

<%
import My.Models
%>
<!-- lots of html code ->
<%
# define a parameter called user of type User
user as User = GetParameter("user")
# now it uses strong typing, instead of reflection
output "<p>${user.Name} - ${user.Email}</p>"
%>

With this simple change, you've completely eliminated the use of reflection and probably increased by a fair margin your application performance. However, because it significantly increases the complexity of developing the views, it is not really the recommended approach. If you run into a situation where the cost of reflection in the views is a significant one, there are other options, which involve improving dynamic dispatch inside of Brail, bringing you the benefits without the cost. This is not implemented currently, because so far we have not run into a situation where this was a problem that warranted the additional complexity.

Note

Beyond making the view code more complex, this can affect the ability to use some of the nicer abilities on Brail, such as ignoring null references using the IgnorNull proxy.

Auto Imports

As you can imagine, it can get tiresome to specify the default imports all over the place, Brail supports automatic imports from the configuration. All you need to do is specify the following in the web.config file:

<Brail>
     	<import namespace="My.Models"/>
</Brail>

And it will be added for you by Brail.

8.5.14. ViewComponent Support

Brail supports the following syntax for ViewComponents:

<%
component MyViewComponent
%>

The above will call MyViewComponent and send any output from the component to the browser.

You can also use view components with arguments. Those arguments are passed via a dictionary (Hash table), like this:

<%
component MyViewComponentWithParams, {"arg" : "value" }
%>

If you want to pass a body to the component, just use the normal colon + indnet to do so:

<% component MyViewComponentWithBody: %>
html content that will be sent to the user if the component will call the RenderBody() method
<% end %>

The contents of a component is evaluated when you call RenderBody , so if you will call RenderBody multiple times, you will send the output of the component's body multiple times as well.

You can also use sections in Brail. Sections are what a way to pass templates to the component in a fine grained manner. Here is a simple example:

<% 
component Grid:
	section Header:
	%>
		<th>Id</th>
		<th>Name</th>
	<%
	end
	section Item:
	%>
		<td>${item.Id}</td>
		<td>${item.Name}</td>
	<%
	end
end %>

8.5.15. Troubleshooting

Brail will throw an exception for any compilation errors, which will include the reason for the error as well as the transformed source code that caused the error, you can use that in order to find out what went wrong.

One thing to be aware of with batch compilation is that if one of your scripts has an error, it will cause the entire batch to fail. Each script in the directory will first try the batch option, and when that fails, it will compile itself as a stand-alone assembly. This can be bad for performance if there are a lot of scripts in a directory. However, a second request for such a script would be served from memory, so it's not too bad.

While it should be possible to debug the views scripts (add System.Diagnostics.Debugger.Break() instead of a breakpoint), I don't recommend it. There is a quite a bit of compiler magic behind Boo as it is, and Brail does its fair share as well. It's likely that you won't have a good experience.

8.5.16. Code Separators

8.5.17. How Brail Works

First of all let's understand where Brail lives. Brail is a View Engine for the Castle MonoRail web development framework. MonoRail is MVC framework for ASP.Net that allows true Separation of Concerns between your business logic and your UI code.

Brail comes into play when it's time to write your UI code, the idea is that instead of using a templating framework, like NVelocity or StringTemplate, you can use a bona fide programming language with all the benefits that this implies. The down side of this approach is that programming languages usually have very strict rules about how you can write code and that is usually the exact opposite of what you want when you write a web page. You want a language that wouldn't get in your way. This is where Brail comes into play.

Brail is based on Boo, a programming language for the .Net Framework which has a very unorthodox view of the place of the user in the language design. Boo allows you to plug your own steps into the compiler pipeline, rip out whatever you don't like, put in things that you want, etc. This means that it packs a pretty punch when you need it. The options are nearly limitless. But enough raving about Boo, it is Brail that you are interested in. What Brail does is to allow you to write your web pages in Boo, in a very relaxed and free way. After you write the code, Brail takes over and transforms it to allow you to run this code. The Brail syntax and options are documented, so we assume that you are already familiar with it.

We need to understand what MonoRail does when it receive a request:

  • The user's browser sends a request to the server: GET: /admin/users/list.rails

  • The ASP.Net runtime passes the request to MonoRail's ProcessEngine, which loads the appropriate controller and after the controller has finished running, it will call to the appropriate view.

  • MonoRail's ProcessEngine calls to Brail passing the current context, the controller and a template name which will usually will look like this: "admin/users/list"

  • Brail processes the request and writes the results back to the user.

Processing Requests

MonoRail receives a request, calls the appropriate controller and then calls to the view engine with the current context, the controller and the view that needs to be displayed. Brail then takes over and does the following:

  • Check if the controller has defined a layout and if it has, pipe the view's output through the layout's output. (The layout is compiled the same way a view is)

  • Get the compiled version of a view script by:

    • Checking if the script is already in the cache. The cache is a hash table ["Full file name of view" : compiled type of the view]

    • If the script is already in the cache but the type is null this means that the view has changed, so we compile just this script again.

    • Instantiate the type and run the instance, which will send the script output to the user.

A few things about changes in the views: Brail currently allows instantaneous replacement of views, layout and common scripts by watching over the Views directory and recompiling the file when necessary, since this is a developer only feature, I'm not worrying too much about efficiency / memory. I'm just invalidating the cache entry or recompiles the common scripts. Be aware that making a change to the Common Scripts would invalidate all the compiled views & layouts in memory and they would all have to be compiled again. This is done since you can't replace an assembly reference in memory.

The interesting stuff is most happening when Brail is compiling a script. For reference, Brail usually will try to compile all the scripts in a directory (but does not recurse to the child directories) in one go, since this is more efficient in regard to speed / memory issues. Occasionally it will compile a script by itself, usually when it has been changed after its directory has been compiled or if the default configuration has been changed. There isn't much difference between compiling a single file and compiling a bunch of them, so I'm just going to ignore that right now and concentrate on compiling a single script. Brail's scripts are actually a Boo file that is being transformed by custom steps that plug into the Boo compiler.

Compiling Scripts

Here is what happens when Brail needs to compile a script:

Creating an instance of BooCompiler, and telling if to compile to memory or to file (configuration option).

  • Adding a reference to the following assemblies: Brail, Castle.MonoRail.Framework, the compiled Common Script assembly and any assembly that the user referenced in the configuration file.

  • Adding imports that were defined in the configuration

  • Run a very simple pre processor on the file, to convert file with <% %> or <?brail ?> to a valid boo script.

  • Remove the default Boo's namespace (this is done because common names such as list, date were introduced including the default namespace and that meant that you couldn't use that as a parameter to the view.

  • Replace any variable reference that has unknown source with a call to GetParameter(variableName) which would use the controller's PropertyBag to get it. GetParameter() throws if it can't find a valid parameter, by the way. The reasoning is that this way you won't get null reference exceptions if you are trying to do something like: date.ToString("dd/mm/yyyy") and the controller didn't pass the date. Since debugging scripts is a pain, this gives you a much clearer message.

  • Then the real transformation begins. Any Brail script is turned into a subclass of the BrailBase class, which provides basic services to the script and allow the engine to output the results to the user. What is happening is that any "free" code, code that isn't in a class / method is moved to a Run() method on the subclass. Any methods are moved to the subclass, so they are accessible from the Run() method. Anything else is simply compiled normally.

When Brail receive a request for a view it looks it up as describe above (from cache/compiled, etc). A new instance of the view is created and its Run() method is called. All the output from the script is sent to the user (directly or via the layout wrapping it.)

BrailBase Class

The BrailBase class has several useful method and properties:

  • ChildOutput - Layouts are scripts that are using their ChildOutput property to wrap their output around the child output. This works as follows, a layout is created, and its ChildOutput is set to a view's output, the view is then run. After the view run, the layout is run and has access to the view's layout.

  • IsDefined(parameterName) - Check if a parameter has been passed, this allows you to bypass GetParameter() throwing if nothing has been passed.

  • OutputSubView() - Output another view.

You can check the source here: https://svn.castleproject.org/svn/castle/trunk/MonoRail/Castle.MonoRail.Views.Brail/BrailBase.cs for the full details

8.6. Composite View Engine

The Composite View Engine just checks whether the view selecteded to be rendered is a .vm file or .aspx file. It then delegates the process to the correct view engine instance: AspNetWebForm or NVelocity.

To enable the Composite View Engine, configure your web.config viewEngine node as follows:

	<viewEngine 
  viewPathRoot="views" 
  customEngine="Castle.MonoRail.Framework.Views.CompositeView.CompositeViewEngine, Castle.MonoRail.Framework.Views.CompositeView" />

8.7. Working With Multiple View Engines

Chapter 9. Filters

9.1. Introduction

Filters are executed before and|or after your actions. It is useful for security, dynamic content and to keep away repetitive code.

9.2. Creating a Filter

To create a filter, create a class that implements the IFilter interface, then associate the filter with your controller.

Note

You can always create an abstract controller class and associate a filter with it and make your controllers extend it.

using Castle.MonoRail.Framework;

public class AuthenticationFilter : IFilter
{
	public bool Perform(ExecuteEnum exec, IRailsEngineContext context, Controller controller)
	{
		if (context.Session.Contains("user"))
		{
			return true;
		}
		else
		{
			context.Response.Redirect("account", "login");
		}

		return false;
	}
}

The Perform return value indicates to the framework if the process should be ended. If you return false no further process will happen for the current request. It is important that you take some action before, like in the example above, issuing a redirect.

The ExecuteEnum parameter says to the filter what is the context of the invocation. It is also used on the FilterAttribute to define when you want to have the filter executed. The possible values are listed on the table below.

ExecuteEnum fields Description
BeforeAction The filter is invoked before the action.
AfterAction The filter is invoked after the action.
AfterRendering The filter is invoked after the rendering.
Always The filter is invoked around all steps.

To associate the filter with the controller, use the FilterAttribute :

using Castle.MonoRail.Framework;

[FilterAttribute(ExecuteEnum.BeforeAction, typeof(AuthenticationFilter))]
public class AdminController : Controller
{
	public void Index()
	{
	}
}

9.3. Ordering

You can always associate more than one filter with a controller. However the order of execution cannot be guaranted. If the order of execution is important, use the ExecutionOrder property. The lower the value, the higher is the priority. For example:

using Castle.MonoRail.Framework;

[FilterAttribute(ExecuteEnum.BeforeAction, typeof(AuthenticationFilter), ExecutionOrder=0)]
[FilterAttribute(ExecuteEnum.BeforeAction, typeof(LocalizationFilter), ExecutionOrder=1)]
public class AdminController : Controller
{
	public void Index()
	{
	}
}

For the example above, AuthenticationFilter runs before the LocalizationFilter .

9.4. Skipping Filters

For some situation you may not want to execute a filter, or all filter, for one or more actions. Use the SkipFilterAttribute for those cases. For example:

using Castle.MonoRail.Framework;

[FilterAttribute(ExecuteEnum.BeforeAction, typeof(AuthenticationFilter), ExecutionOrder=0)]
[FilterAttribute(ExecuteEnum.BeforeAction, typeof(LocalizationFilter), ExecutionOrder=1)]
public class AdminController : Controller
{
	[SkipFilter]
	public void Index()
	{
	}

	[SkipFilter(typeof(LocalizationFilter))]
	public void Create()
	{
	}

	public void Update()
	{
	}
}

For the example above we have defined that:

  • No filters will be executed on the Index action

  • The LocalizationFilter will not be executed on the Create action

  • All filters will run on the Update action

9.5. Passing Parameters

More advanced scenarios might arise where you parameterize a filter. For example, you can create a filter that is able to load text files and add each line of text to the PropertyBag . The file name is not fixed.

The first thing to do is to create a new attribute that extends FilterAttribute :

using Castle.MonoRail.Framework;

[AttributeUsage(AttributeTargets.Class, AllowMultiple=false, Inherited=true), Serializable]
public class MyCoolFilterAttribute : FilterAttribute
{
	private readonly string fileName;

	public MyCoolFilterAttribute(String fileName) : base(ExecuteEnum.BeforeAction, typeof(CoolFilterImpl))
	{
		this.fileName = fileName;
	}
	
	public string FileName
	{
		get { return fileName; }
	}
}

As you can see, the custom attribute inherits from FilterAttribute and configures the filter on the user's behalf.

Now we just need to implement the filter itselt. We also need to signalize to the framework that we are interested in gaining access to the attribute instance as we will extract information from it. This is done using the IFilterAttributeAware interface.

using Castle.MonoRail.Framework;

public class CoolFilterImpl : IFilter, IFilterAttributeAware
{
	private MyCoolFilterAttribute attribute;

	// Implementation of IFilterAttributeAware
	public FilterAttribute Filter 
	{ 
		set { attribute = (MyCoolFilterAttribute) value; } 
	}
	
	// Implementation of IFilter
	public bool Perform(ExecuteEnum exec, IRailsEngineContext context, Controller controller)
	{
		// Now you can access the parameters:
		String fileName = attribute.FileName; 
		
		// Work
		
		// Allow the process to go on
		return true;
	}
}

Now using the filter is very simple:

using Castle.MonoRail.Framework;

[MyCoolFilterAttribute("customer_messages.txt")]
public class CustomerController : Controller
{
	public void Index()
	{
	}
}

Chapter 10. Layouts

10.1. Introduction

Layouts allow you to template your site by specifying common html and controls, such as structural html and navigation controls, in one place that are available for any view to use.

Layouts are just standard views, but they need to be created in a folder named layouts , notice that it is plural. The layouts folder needs to be directly under your views root directory.

Note

Note that the extension for the layout files need to match whatever view engine you are using, such as .aspx . ASP.NET users that are tempted to use the master page model and use .master will be sadly dissappointed with a 'resource cannot be found' error.

10.2. Using Layouts

You can associate a layout with a controller or with an action using the LayoutAttribute . For example:

using Castle.MonoRail.Framework;

[Layout("application")]
public class CustomerController : Controller
{
	public void Index()
	{
	}
}

In some scenarios you might want to turn off the layout processing. To do so use CancelLayout method. There are other cases where you want to render a specific view and turn off layout at the same time. The RenderView and RenderSharedView have overloads to allow you to do that.

Method
RenderView(String name, bool skipLayout)
RenderView(String controller, String name, bool skipLayout)
RenderSharedView(String name, bool skipLayout)

For example:

using Castle.MonoRail.Framework;

[Layout("application")]
public class CustomerController : Controller
{
	public void Index()
	{
		RenderView("welcome", true);
	}
}

Each view engine uses a specific approach to have layouts working. We will instruct on how to deal with layouts on the view engines documents.

Chapter 11. Rescues

11.1. Introduction

A rescue is an association of a special view that will only be rendered if an exception happens. The view file must be present in a rescues folder directly under your views folder.

Note

A sample demonstrating the concepts in the section can be downloaded from !download/monorail/trunk/MonoRail.SendingEmailSample.zip

11.2. Using Rescues

A rescue can be associated with a controller or per action. You can also bound a rescue with an exception. If the action throws an exception (the action cannot swallon the exception), MonoRail will match the rescue definition that is closely related to the exception type and use the specified view.

To create an association you must use the RescueAttribute . For example:

using Castle.MonoRail.Framework;

[Rescue("dberror", typeof(System.Data.SqlException))]
public class ProductController : Controller
{
	[Rescue("commonerror")]
	public void Index()
	{
		throw new System.Data.SqlException("fake error");
	}

	[Rescue("dumbprogrammer", typeof(DivideByZeroException))]
	public void List()
	{
		int val = 0;
		int x = 10 / val;
	}

	public void Search()
	{
	}
}

The usage of the RescueAttribute in the example above define the following rules:

  • If any action throws a SqlException , the view view/rescues/dberror will be selected

  • If the action Index throws any kind of exception (including SqlException ), the view view/rescues/commonerror will be selected. This overrides the definition in the controller level.

  • If the action List throws a DivideByZeroException , the view view/rescues/dumbprogrammer will be selected.

Whenever an exception happens, the MonoRail context (which is per request) will populate the property LastException so your view can show the exception details.

Chapter 12. Authentication and Authorization

12.1. Introduction

MonoRail does not provide a standard way to accomplish authentication not authorization. This is intentional as MonoRail runs on top of Asp.Net infrastructure which provides standard way to handle both, like the FormsAuthentication and the <authorization> configuration element.

That being said, you can also use Filters to implement authentication if you want. All you have to do is associate an authentication filter with the controllers that can only be accessed by authenticated users.

12.2. Forms Authentication

Note

A sample demonstrating the concepts in the section can be downloaded from !download/monorail/trunk/MonoRail.AuthenticationUsingForms.zip

When you use FormsAuthentication you leverage to Asp.Net to handle the authentication, or the cookie and principal implementation or both.

If you want to use it, first thing to do is say which resources can only be accessed by authenticated users. Note that from Asp.Net point of view, a controller is also a resource.

To configure that use <authorization> configuration element on the web.config (we are using it on web.config on the root folder).

<?xml version="1.0" encoding="utf-8"?>
<configuration>

	<location path="home">
		<system.web>
			<authorization>
				<deny users="?"/>
			</authorization>
		</system.web>
	</location>

	...

With the configuration above we are saying that the controller Home cannot be reached by anonymous users.

Now that the resources are protected we can configure the FormsAuthentication support. In order to be really simple, we even manage the usernames and passwords allowed on the configuration file too:

<?xml version="1.0" encoding="utf-8"?>
<configuration>
	
	...

    <system.web>
		<authentication mode="Forms">
			<forms name="auth" loginUrl="login/index.rails">
				<credentials passwordFormat="Clear">
					<user name="admin" password="admin" />
					<user name="user" password="user" />
				</credentials>
			</forms>
		</authentication>

		...

The configuration defines that in the event of authentication needed, Asp.Net should redirect the user to login/index.rails which is our LoginController , action index . There we present our login form:

The authentication code relies on the FormsAuthentication :

using System.Web.Security;

using Castle.MonoRail.Framework;

[Layout("default")]
public class LoginController : SmartDispatcherController
{
	public void Index()
	{
	}
	
	public void LogIn(String username, String password, bool rememberme, string ReturnUrl)
	{
		if (FormsAuthentication.Authenticate(username, password))
		{
			CancelView();
			
			FormsAuthentication.RedirectFromLoginPage(
				username, rememberme, Context.ApplicationPath);
		}
		else
		{
			// If we got here then something 
			// is wrong with the supplied username/password
			
			Flash["error"] = "Invalid user name or password. Try again.";
			RedirectToAction("Index", "ReturnUrl=" + ReturnUrl);
		}
	}
}

Once authenticated, we can navigate to the protected resource:

12.3. Filters

Note

A sample demonstrating the concepts in the section can be downloaded from !download/monorail/trunk/MonoRail.AuthenticationUsingFilters.zip

When using filters the options are wide. You can rely on the session, you can rely on cookies; you may want to provide an implementation of IPrincipal and supply the roles yourself to have a more fine-grained permission control.

The approach demonstrated here uses a custom implementation of IPrincipal on the User class and uses the session to persist the authentication among requests. Implementing a "remember me" feature would require a cookie. It was not implemented to keep the example as simple as possible.

Authentication control with filter is just a matter of associating an authentication filter with the controllers you do not want to be accessed by anonymous users. This only restrict access to controller's actions. If you want to protect files, you might use a mix of filters and FormsAuthentication or implemeting the Authenticate_Request event.

In our example we want to prevent access to the HomeController . So we associate a BeforeAction filter with it:

using AuthenticationUsingFilters.Filters;
using Castle.MonoRail.Framework;

[Layout("default")]
[Filter(ExecuteEnum.BeforeAction, typeof(AuthenticationFilter))]
public class HomeController : SmartDispatcherController
{
	public void Index()
	{
	}
}

The filter implementation can do a number of things to check if the current user is authenticated. We decided to check if an existing object exists in the session. The object implements IPrincipal but this is not required. The implementation will vary depending on the requirements and how you plan to handle authorization.

using System.Collections.Specialized;
using AuthenticationUsingFilters.Model;
using Castle.MonoRail.Framework;

public class AuthenticationFilter : IFilter
{
	public bool Perform(ExecuteEnum exec, IRailsEngineContext context, Controller controller)
	{
		// Read previous authenticated principal from session 
		// (could be from cookie although with more work)
		
		User user = (User) context.Session["user"];
		
		// Sets the principal as the current user
		context.CurrentUser = user;
		
		// Checks if it is OK
		if (context.CurrentUser == null || 
			!context.CurrentUser.Identity.IsAuthenticated)
		{
			// Not authenticated, redirect to login
			NameValueCollection parameters = new NameValueCollection();
			parameters.Add("ReturnUrl", context.Url);
			controller.Redirect("login", "index", parameters);
			
			// Prevent request from continue
			return false;
		}
		
		// Everything is ok
		return true;
	}
}

The LoginController will not be much different from the previous example:

using AuthenticationUsingFilters.Model;
using Castle.MonoRail.Framework;

[Layout("default")]
public class LoginController : SmartDispatcherController
{
	public void Index()
	{
	}
	
	public void LogIn(String username, String password, bool rememberme, string ReturnUrl)
	{
		// We should authenticate against a database table or something similar
		// but here, everything is ok as long as the 
		// password and username are non-empty
		
		if (IsValid(username, password))
		{
			CancelView();
			
			// Ideally we would look up an user from the database
			// The domain model that represents the user
			// could implement IPrincipal or we could use an adapter
			
			User user = new User(username, new string[0]);
			
			Session["user"] = user;
			
			Redirect(ReturnUrl);
		}
		else
		{
			// If we got here then something is wrong 
			// with the supplied username/password
		
			Flash["error"] = "Invalid user name or password. Try again.";
			RedirectToAction("Index", "ReturnUrl=" + ReturnUrl);
		}
	}

	private bool IsValid(string username, string password)
	{
		return username != null && password != null;
	}
}

If the authentication passes we just create the User and add it to the session, allowing the filter to get it for the subsequent requests.

The User class is just a simple implementation of IPrincipal . Real application will use an adapter or change an existing class that represents a logged user, or system user to implement it as well.

public class User : IPrincipal
{
	private string[] roles;
	private IIdentity identity;

	public User(String name, String[] roles)
	{
		identity = new GenericIdentity(name, "Custom MonoRail authentication");
		this.roles = roles;
	}

	public bool IsInRole(string role)
	{
		return Array.IndexOf(roles, role) >= 0;
	}

	public IIdentity Identity
	{
		get { return identity; }
	}
}

Compared with the previous example, the data outputted is a little bit different:

12.4. Using PrincipalPermission

If you have a custom implementation of IPrincipal or even if you use the GenericPrincipal but supply the roles, you would be able to use PrincipalPermission to prevent users from invoking methods or code branches.

Note

In addition to set the principal implementation on the HttpRequest (which is what context.CurrentUser does) you must also se the principal on the current thread by using System.Threading.Thread.CurrentPrincipal .

The PrincipalPermission and PrincipalPermissionAttribute belongs to the .Net security infrastructure and demands that the executing principal have a specific role. This can be used as the last resource to secure your application.

For example, suppose your application is clever enough to not offer to user button or links to resources/actions the users do not have access to. However, this is commonly refered to as security by obscurity, as an user that knows how to get to the resource will be able to access them. To secure the application from this kind of access you might use the PrincipalPermission . For example:

using System.Security;
using Castle.MonoRail.Framework;

public class OrderController : SmartDispatcherController
{
	[PrincipalPermission(SecurityAction.Demand, Role="Administrator")]
	public void DeleteOrder(int orderid)
	{
		...
	}
}

If the current user does not have the role Administrator a SecurityException will be thrown.

12.5. The SecurityView Component

MonoRail comes with a ViewComponent called SecurityComponent which can be used on views to prevent rendering a content based on the roles the current user (principal) has. For example:

#blockcomponent(SecurityComponent with "role=Administrator")

This content can only be seen by administrators

#end

For more information on ViewComponents see the Reusing UI portions (ViewComponents) document.

Chapter 13. Helpers

13.1. Introduction

Helpers are associated with a controller and made available to be used on the view. They are usually used to reuse some generation code.

13.2. Creating Customer Helpers

A helper is just an ordinary class. It might optionally extend AbstractHelper in order to have access to the controller instance and some utility methods. For example:

public class MyHelper
{
	public String BuildUserLink(User user)
	{
		return String.Format("<a href='/users/showuser.rails?id={0}'>{1}</a>", 
			user.Id, user.Name);  
	}
}

The helper must be associate with the controller whose views might use it. This is done using the HelperAttribute :

using Castle.MonoRail.Framework;

[Helper(typeof(MyHelper))]
public class MemberController : Controller
{
	public void List()
	{
		PropertyBag.Add("users", ObtainUsers());
	}
}

Now it is just a matter of using the helper by its name:

#foreach ($user in $users)
	$MyHelper.BuildUserLink(${user})
#end

13.3. Built In Helpers

13.3.1. FormHelper

The FormHelper allows you to output Html Input elements using the conventions necessary to use the DataBinder on the server side. It also queries the objects available on the context to automatically populate values correctly, saving you the burden of filling inputs, selects, checkboxes and radios.

Note

A sample demonstrating the concepts in the section can be downloaded from !download/monorail/trunk/MonoRail.FormHelperSample.zip

The helper is inspired on two exquisite works: the Ruby on Rails' FormHelper and the Apache Jakarta Taglibs. The idea is to generate html form elements while:

  • Using the same naming convention used by the DataBinder on the controller side

  • Collect the value from an instance in the context (if available) and populate the html element with the value or selection

When using the FormHelper you specify a target through a string. The target is evaluated and if the object is available in the context then the evaluation result is used to provide the correct output. For a simple text input element, the evaluated value will be the value of the textbox.

Getting Started

Using the FormHelper is easy and in a few minutes you will understand how it works. Just remember that it requires that the controller and the view work together.

The goal is to generate proper Html elements that can be easily databound on the controller side. The most trivial case is binding single values. For example, you can have a view like the following (using NVelocity View Engine):

<form action="Index.rails" method="post">
$FormHelper.TextField("name")
$FormHelper.TextField("address")
</form>

Whenever this view is rendered, the FormHelper 's TextField method will be invoked. The first thing it does is extract the root target. In the case above the targets are not chained, so the roots will be name and address .

After that it will search in the context dictionaries for the roots, in the following order:

  • PropertyBag

  • Flash

  • Session

  • Request.Params

  • HttpContext.Items

If it finds the entry, it will extract the value and use it. If it doesn't find the entry, no value will be set on the html element.

Suppose the action using the view above (the form definition) is the following:

public void Index()
{
}

For this case, the html output will be:

<form action="Index.rails" method="post">
	<input type="text" id="name" name="name" value="" />
	<input type="text" id="address" name="address" value="" />
</form>

Note the "value" property of both input elements is blank.

However, if the action was:

public void Index()
{
    PropertyBag.Add("name", "hammett");
    PropertyBag.Add("address", "pereira leite, 44");
}

Then the FormHelper would have the values filled:

<form action="Index.rails" method="post">
	<input type="text" id="name" name="name" value="hammett" />
	<input type="text" id="address" name="address" value="pereira leite, 44" />
</form>

The support for autmatically populating Html elements goes beyond input fields. See below for more information on the FormHelper .

Complex Objects

Consider a different action code now:

public void Index()
{
    PropertyBag.Add("contact", new Contact("john", "some address", "phone number") );
}

A view for this

<form action="Index.rails" method="post">
	$FormHelper.TextField("contact.name")
	$FormHelper.TextField("contact.address")
	$FormHelper.TextField("contact.phone")
</form>

The FormHelper will find the contact entry in the PropertyBag , and then find the properties name , address and phone . It will extract the values from the item in the PropertyBag and render the view accordingly:

<form action="Index.rails" method="post">
	<input type="text" id="contact_name" name="contact.name" value="john" />
	<input type="text" id="contact_address" name="contact.address" value="some address" />
	<input type="text" id="contact_phone" name="contact.phone" value="phone number" />
</form>

Arrays

Arrays are also supported. Suppose you have the following action code:

public void Index()
{
	PropertyBag.Add("list", new string[] 
	{
		"value 1", "value 2"
	} );

    PropertyBag.Add("contacts", new Contact[] 
	{ 
		new Contact("john", "address 1", "phone number 1"),
		new Contact("mary", "address 2", "phone number 2")
	} );
}

In this case you have to use the indexed name convention as the target so FormHelper can know from which index it should extract the value:

<form action="Index.rails" method="post">

$FormHelper.TextField("list[0]")
$FormHelper.TextField("list[1]")

$FormHelper.TextField("contacts[0].name")
$FormHelper.TextField("contacts[0].address")
$FormHelper.TextField("contacts[0].phone")

$FormHelper.TextField("contacts[1].name")
$FormHelper.TextField("contacts[1].address")
$FormHelper.TextField("contacts[1].phone")

</form>

The FormHelper will use the index value to find the correct entry. It will generate something like the following:

<form action="Index.rails" method="post">

<input type="text" id="list_0_" name="list[0]" value="value 1" />
<input type="text" id="list_1_" name="list[1]" value="value 2" />

<input type="text" id="contact_0_name" name="contact[0].name" value="john" />
<input type="text" id="contact_0_address" name="contact[0].address" value="address 1" />
<input type="text" id="contact_0_phone" name="contact[0].phone" value="phone number 1" />

<input type="text" id="contact_1_name" name="contact[1].name" value="mary" />
<input type="text" id="contact_1_address" name="contact[1].address" value="address 2" />
<input type="text" id="contact_1_phone" name="contact[1].phone" value="phone number 2" />

</form>

Working with Sets

FormHelper supports a broad range of scenarios when dealing with sets. Sets are used when Select and CheckboxList are generated. It is adamant that you know how it works when dealing with sets.

For Select and CheckboxList generation, the common target is considered the initial selection set . The value supplied as the data source is treated as the complete set. The initial selection must be a subset of the data source set.

This is very obvious and works nicely when both sets are composed of primites and both sets have same types. But not every application has this scenario to work with.

The FormHelper makes a few verifications which help it to decide on an approach based on the sets available. See the image below.

If the types are not primitive types, it is up to the programmer to inform the property the element has that identifies the instance, using the value parameter. The programmer can also specify the text parameter which is used to identify the property that returns a descriptive text about the element instance.

The sourceProperty parameter is used to identity the property that should be used on the html element. This is only helpful when the types are different and the identification property on the data source element is different from the identification property on the initial selection elements.

The suffix parameter is used to override the suffix used on the generate element names. FormHelper will always try to use the value specified for the value parameter which is right for almost all situations, but one: when you are dealing with different types on the sets but the initial set is null and the FormHelper won't be able to identify that. So if you have different types and you know in advance that the initial set can be empty or null, specify the suffix parameter.

Generating Selects

FormHelper is able to generate single and multi-value selects. Make sure you have read the How FormHelper works with sets document.

Single Value Selects

To create a select just specify the target and the data source which would be a set of all elements so it can create options for.

Consider the following action code:

public void Index()
{
	// data source
	PropertyBag["primenumbers"] = new int[] { 2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47 };
	
	// initial selection
	PropertyBag["oneprime"] = 3;
}

The code on the view to use it would be:

$FormHelper.Select("oneprime", $primenumbers) 

With complex objects you must care to specify the value and text parameters so the FormHelper can generate proper html elements.

Consider the following action code:

public void Index()
{
	// data source
	IList authors = new Author[] 
	{ 
		new Author(1, "hammett"), 
		new Author(2, "john doe"), 
		new Author(3, "someone else")
	};
	PropertyBag["authors"] = authors;
	
	// initial selection
	Blog blog = new Blog();
	blog.Author = new Author(1, "hammett");
	PropertyBag["blog"] = blog;
}

The Author class above has an Id property and Name property. So here is how the view would use it:

$FormHelper.Select("blog.author.id", $authors, "%{value='id', text='Name', firstoption='Please select'}")

Note the use of firstoption parameter. It includes the specified content as the first available option on the select.

Multi-Value Selects

Multi-value selects are not very different from what we have seen so far. The initial selection however will be a set.

Consider the following action code:

public void Index()
{
	// data source
	PropertyBag["primenumbers"] = new int[] { 2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47 };
	
	// initial selection
	PropertyBag["multipleprimes"] = new int[] { 3, 5, 7 };
}

Here is how the view would use it:

$FormHelper.Select("multipleprimes", $primenumbers, "%{multiple='true', style='width: 110px; height: 90px;'}")  

Note that the multiple parameter is required to generate a html element prepared for multiple selection.

Creating Checkbox Lists

FormHelper allows you to generate a list of checkboxes associated with a data source. It is also able to pre-check the checkboxes based on the initial selection set. Make sure you have read the How FormHelper works with sets document.

Listing checkboxes requires a state object. To create it you invoke CreateCheckboxList which returns a CheckboxList instance. A CheckboxList is enumerable and exposes an Item and a LabelFor method. You must enumerate the elements and while doing it invoke Item which returns the checkbox element. LabelFor adds a label-tag to the caption that allows the user to click the caption as well as the checkbox itself.

Consider the following action code:

public void Index()
{
	// data source
	PropertyBag["primenumbers"] = new int[] { 2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47 };
	
	// initial selection
	PropertyBag["selectedPrimes"] = new int[] { 11, 19, 29 };
}

The code on the view to use it would be:

<p>
#set($items = $FormHelper.CreateCheckboxList("selectedPrimes", $primenumbers))

#foreach($elem in $items)
  $items.Item() $items.LabelFor($elem) <br/>
#end
</p>

With complex objects you must care to specify the value parameter so the FormHelper can generate proper html elements.

Consider the following action code:

public void Index()
{
	// data source
	Category[] categories = new Category[] 
	{ 
		new Category(1, "Music"), 
		new Category(2, "Humor"), 
		new Category(3, "Politics")  
	};
	PropertyBag["categories"] = categories;
	
	// initial selection
	Blog blog = new Blog();
	blog.Categories = new Category[] { new Category(2, "Humor") };
	PropertyBag["blog"] = blog;
}

The Category class above has an Id property and Name property. So here is how the view would use it:

<p>
#set($items = $FormHelper.CreateCheckboxList("blog.categories", $categories, "%{value='Id'}"))

#foreach($elem in $items)
  $items.Item()  $elem.Name   <br/>
#end
</p>

13.3.2. UrlHelper

The UrlHelper exposes methods to easily build URL's and HTML anchors in your views. The UrlHelper uses context-sensitive information (such as the currently configured Monorail extension) to build proper URLs.

Generating URLs

The For method generates a URL from the specified parameters. For example:

$UrlHelper.For("%{action='Save'}")

This generates a complete URL for a controller and and action. With no controller specified, the controller from the current context is used. The action was defined in the parameter list as "Save".

All of the UrlHelper methods accept a set of parameters to control the building of the URL. The list of possible parameters are defined below.

ParameterDefinition
area The area of the controller. The default is the current area.
controller The controller for the URL. The default is the current controller.
action The controller's action.
protocol The protocol to use.
port The port number to use.
domain The domain of the URL.
subdomain The subdomain.
appVirtualDir The virtual directory.
extension The extension for the URL. If not specified, the configured Monorail extension is used.
absolutePath If set to true, define an absolute path.
applySubdomain If set to true, use the subdomain.
suffix The suffix.

Generating Links

Use the Link method to generate a complete HTML link.

$UrlHelper.Link("Edit Trainer", "%{action='Edit', 'querystring='id=$trainer.id'}")

The above produces a link that would look like this (assuming the current controller is called "Trainer":

<a href="/virtualdir/Trainer/Edit.castle?id=45">Edit Trainer</a>

In the example above, the extension ".castle" is pulled from the current Monorail configuration.

You may also add a second set of parameters used to add atributes to the generated anchor:

$UrlHelper.Link("Edit Trainer", "%{action='Edit', 'querystring='id=$trainer.id'}", "%{target='_new'}")

Produces:

<a href="/virtualdir/Trainer/Edit.castle?id=45" target="_new">Edit Trainer</a>

Generating Button Links

The $UrlHelper.ButtonLink method works the same way as the $UrlHelper.Link method, except an HTML button is generated with some javascript to handle the onclick event to navigate to the URL.

13.3.3. AjaxHelper

MonoRail supports Ajax by using the prototype jslib .

First of all, to use Ajax support you must make the javascript code available to your view:

$AjaxHelper.GetJavascriptFunctions()

Which will render:

<script type="text/javascript" src="/MonoRail/Files/AjaxScripts.rails"></script>

This helper also exposes the Behaviour js library. To use it, invoke GetBehaviourFunctions :

$AjaxHelper.GetBehaviourFunctions()

Common Parameters

The more you know about the prototype library, the better. We recommend the Developer Notes for prototype.js although it is a little outdated.

The prototype library has two main classes to perform remote requests:

  • Ajax.Request : performs a remote invocation and allow you to work on the results with callbacks

  • Ajax.Updater : extends the Ajax.Request and updates a html element with the invocation result.

The following parameters can be used on both Ajax.Request and Ajax.Updater :

ParameterDescription
url The url to be invoked. You cannot specify parameters (like url?key=value). If you need to pass parameters, use the with parameter.
method Http method to be used on the invocation. Defaults to 'post'.
with Defines the parameters to be send with the request. For example name=hammett&age=27&iscustomer=true
form If you omit the with parameter but include the form then it will generate code to serialize the current form. Equivalent to use with=Form.serialize(this) .

When you specify the parameter update or success or failure , the AjaxHelper will generate an Ajax.Updater call. The following parameters applies to it:

ParameterDescription
update Defines the name of the html element that will be updated with the return xml of the request. Usually a div is used.
success / failure Defines the name of the html element that will be updated conditionally with the return xml of the request. If the request is successful, the elemented pointed by success will be updated, otherwise it will use the elemented pointed by failure .
evalScripts Defines whether the returned xml should have its javascript content evaluated. Defaults to true .
position Defines a strategy to insert the resulting xml on the DOM. The supported values are Before , Top , Bottom and After .

Callbacks can also be used. The prototype will invoke the specified javascript functions during different steps in the remote invocation.

ParameterDescription
Loading Called when the remote document is being loaded with data by the browser.
Loaded Called when the browser has finished loading the remote document.
Interactive Called when the user can interact with the remote document, even though it has not finished loading.
Complete Called when the XMLHttpRequest has completed.
OnSuccess Called when the request was successfully (Status code < 500)
OnFailure Called when the request was not successfully (Status code >= 500)

You can also specify that a function must be executed before, after or define it as a condition to the Ajax request be issue.

ParameterDescription
before Defines that the specified javascript function must run before the Ajax request is sent.
after Defines that the specified javascript function must run right after the Ajax request is sent.
condition Defines that the specified function must return true to allow the Ajax request to take place.

Note that the OnSuccess and OnFailure callbacks will include a parameter called request which is the original XmlHttpRequest object. Your callback function will need to have a parameter called request to operate properly.

Using It

The best and easiest way to use the AjaxHelper is to check the API documentation to identify the method you want. Check its signature. Most of them will have an IDictionary parameter. This is an approach to make the ajax usage on views more self-documented.

The method API documentation should highlight required parameters or special meanings that a parameter might have. Note that the common parameters discussed above applies to most of the methods on AjaxHelper.

The following snippets shows the AjaxHelper in action.

$AjaxHelper.LinkToRemote("Show server time", "showtime.rails", "%{update='maindiv', OnSuccess='showSuccessMessage(request)'}") 

Using the Behavior Library

The Behaviour javascript library allow you to use CSS selectors to bind object's events to javascript functions.

The AjaxHelper exposes a few methods related to Behaviour.

If you change your document object model dynamically you can reapply the rules defined within Behavior. In this case use ReApply which renders a script block invoking Behavior.apply() .

Behavior binds itself to the window.onload event. In the case you want to use it as well, invoke AddLoadEvent(String loadFunctionName) .

Registering Rules

The following is an example of how to associate javascript functions to events using Behaviour:

var myrules = {
	'b.someclass' : function(element){
		element.onclick = function(){
			alert(this.innerHTML);
		}
	},
	'#someid u' : function(element){
		element.onmouseover = function(){
			this.innerHTML = "BLAH!";
		}
	}
};

If you would prefer to use the helper to generate the source, you can do something like the following:

$AjaxHelper.StartBehaviourRegister()
$AjaxHelper.Register("b.someclass", "onclick", "functionName")
$AjaxHelper.Register("#someid u", "onmouseover", "function(){ this.innerHTML = 'BLAH!'; }")
$AjaxHelper.EndBehaviourRegister()

You may also consult the API documentation for the AjaxHelper .

Javascript Action Proxies

MonoRail includes the ability to generate a proxy object in javascript to invoke actions on controllers. For example, suppose you have the following controller:

using Castle.MonoRail.Framework;

public class AdminController : SmartDispatcherController
{
	public void Index()
	{
	}

	[AjaxAction]
	public void DisableUser(int userId)
	{
		// Do something important here
		RenderText("Done");
	}

	[AjaxAction]
	public void ChangeUserPassword(int userId, String newPassword)
	{
		// Do something important here
		RenderText("Done");
	}
}

The methods you want to generate proxies for need to be marked with the attribute AjaxActionAttribute .

On the view side, you can use the following methods to generate a javascript block that uses AjaxRequest class to invoke the actions.

  • AjaxHelper.GenerateJSProxy(string proxyName)

  • AjaxHelper.GenerateJSProxy(string proxyName, string controller)

  • AjaxHelper.GenerateJSProxy(string proxyName, string area, string controller)

In the view for the Index action of AdminController we can generate a proxy:

$AjaxHelper.GetJavascriptFunctions()
$AjaxHelper.GenerateJSProxy("myproxy")

The GenerateJSProxy call will generate a js block that uses Ajax.Request to make a remote invocation:

<script type="text/javascript" src="/MonoRail/Files/AjaxScripts.rails"></script>
<script type="text/javascript">var myproxy =
{ 

	DisableUser:
	function(userId, callback)
	{
		var r=new Ajax.Request('/admin/DisableUser.rails',
		{
			parameters: '_=x26userid=' + userId,
			asynchronous: !!callback,
			onComplete: callback
		});
		if(!callback) return r.transport.responseText;
	}
,
	ChangeUserPassword:
	function(userId, newPassword, callback)
	{
		var r=new Ajax.Request('/admin/ChangeUserPassword.rails',
		{
			parameters: '_=&userId=' + userId + '&newPassword=' + newPassword,
			asynchronous: !!callback,
			onComplete: callback
		});
		if(!callback) return r.transport.responseText;
	}
}
</script>

As you see it supports synchronous and asynchronous calls. If you specify a callback function it will be asynch, otherwise synchronous. The use of the remote method becomes natural js code:

<input type="button" onclick="javascript:myproxy.DisableUser($('userid'));" />

You may also consult the API documentation for the AjaxHelper .

LinkToFunction and ButtonToFunction

The LinkToFunction and ButtonToFunction methods allow the generation of Html elements that once clicked invoke a javascript function. It is very simple and has nothing to do with remote invocations.

You may also consult the API documentation for the AjaxHelper .

LinkToRemote and ButtonToRemote

The LinkToRemote and ButtonToRemote generates html elements that once clicked invokes a remote action (usually a controller's action).

The following overloads are supported:

  • ButtonToRemote(String innerContent, String url, IDictionary options)

  • ButtonToRemote(String innerContent, String url, IDictionary options, IDictionary htmloptions)

  • LinkToRemote(String name, String url, IDictionary options)

  • LinkToRemote(String name, String url, IDictionary options, IDictionary htmloptions)

Remote Form

The BuildFormRemoteTag generates a form element that instead of sending the content in the normal way, it uses Ajax to send the data.

The following overloads are supported:

  • BuildFormRemoteTag(String url, IDictionary options)

  • BuildFormRemoteTag(IDictionary options)

Observers

The observers can be associated with form elements or with the whole form. An ajax invocation is sent when a change is detected.

The following overloads are supported:

  • ObserveField(String fieldId, int frequency, String url, String idOfElementToBeUpdated, String with)

  • ObserveField(String fieldId, int frequency, String url, IDictionary options)

  • ObserveField(IDictionary options)

  • ObserveForm(String formId, int frequency, String url, String idOfElementToBeUpdated, String with)

  • ObserveForm(String formId, IDictionary options)

  • ObserveForm(IDictionary options)

You may also consult the API documentation for the AjaxHelper .

Periodical Updates

The PeriodicallyCallRemote makes remote invocations with a specified frequency.

  • PeriodicallyCallRemote(IDictionary options)

  • PeriodicallyCallRemote(String url, IDictionary options)

Autocompletion

The AutoComplete enables a google style search where partial searches are emitted as you type.

It is higly advisable to carefully read the script.aculo.us documentation for the AutoCompleter to know about the possible completionOptions.

AjaxHelper offers the following methods for providing AutoCompletion:

  • InputTextWithAutoCompletion(IDictionary parameters, IDictionary tagAttributes)

  • InputTextWithAutoCompletion(String inputName, String url, IDictionary tagAttributes, IDictionary completionOptions)

  • AutoCompleteInputText( String elementId, String url, IDictionary options )

FormHelper also offers a combination of a input field with databinding behaviour and Ajax Autocomplete behaviour.

  • FormHelper.TextFieldAutoComplete(string target, string url, IDictionary tagAttributes, IDictionary completionOptions)

This field is created analogous to InputTextWithAutoCompletion , but instead of an inputName, the target object's property path is specified (i.e. user.Role ).

Usage

Ok, let me directly start with the code:

#set ($ajaxOpt = "%{parameters='{user:\'$user.Id\'}', paramName='\'search\''}")
#set ($inputOpt = "%{class='txt'}")
<p>Enter your favorite programming frameworks:</p>

<form action="$UrlHelper.For("%{action='EnterDataResults'}")" method="post">
	<input type="hidden" name="user" value="$user.Id" />
	<table class="blind">
		<tr>
			<td>IoC-Framework</td>
			<td>$FormHelper.TextFieldAutoComplete("preference.ioc", UrlHelper.For("%{action='LookupIoc'}"), inputOpt, ajaxOpt)</td>
		</tr>
		<tr>
			<td>OR/M-Framework</td>
			<td>$FormHelper.TextFieldAutoComplete("preference.orm", Url.ForHelper("%{action='LookupOrm'}"), inputOpt, ajaxOpt)</td>
		</tr>
		<tr>
			<td></td>
			<td><input type="submit" value="Save Data" /></td>
		</tr>
	</table>	
</form>

Now to the explanation.

The variable ajaxOpt shows how to specify options for the Scriptaculous AutoCompleter. The AutoCompleter wants JSON , so you need to make sure that string literals are enclosed in quotation marks both in Brail/NVelocity and in the value itself.

In this example, the completion options are used to add a custom parameter to the query, which will be used by the controller called by the AutoCompleter. Additionally the name of the parameter is changed. If it is left unchanged, AutoCompleter would use the name attribute of the input element, in this case preference.ioc or preference.orm , which is normally unwanted due to databinding that should not occur at this point.

inputOpt simply defines options for the input tag and should be straightforward.

The last thing to do on the calling page is to call the helper method.

Next is the AJAX controller:

public void LookupIoc([ARFetch("user")]User user, string search)
{
	// perform search and put results into PropertyBag
}

And the associated view

<ul>
#foreach ($ioc in $frameworks)
	<li>$ioc.Name</li>
#end
</ul>

This is straight-forward. Format the results as an unordered list. The AutoCompleter uses this list to build the contents of the completion widget.

13.3.4. Behavior Helper

13.3.5. ScriptaculousHelper

13.3.6. PaginationHelper

The PaginationHelper simplify the creation of paginated navigation on items.

Standard Paging

Suppose you have a set with 400 items:

public class MyController : SmartDispatcherController
{
	public void List()
	{
		PropertyBag["items"] = ObtainMyLargeCollection();
	}

	private IList ObtainMyLargeCollection()
	{
		...
	}
}

With the PaginationHelper you can return a sliced representation of the collection:

public class MyController : SmartDispatcherController
{
    public void List()
    {
        PropertyBag["items"] = PaginationHelper.CreatePagination( ObtainMyLargeCollection(), 10 );
    }

    private IList ObtainMyLargeCollection()
    {
        ...
    }
}

The help will look for a page on Request.Params to define its starting index. The CreatePagination method returns a IPaginatedPage object which exposes several properties so you can build a nice pagination box:

  • FirstIndex

  • CurrentIndex

  • LastIndex

  • PreviousIndex

  • NextIndex

  • HasPrevious

  • HasNext

  • HasFirst

  • HasLast

  • FirstItem

  • LastItem

  • TotalItems

This a snippet that renders the items and the pagination box:

#foreach($row in $items)
  <tr>
    <td>$row.Name</td>
    <td>$row.Email</td>
  </tr>
#end</table>

<div class="pagination">
<table width="100%" border="0">
  <tr>
  <td>Showing $items.FirstItem - $items.LastItem of $items.TotalItems</td>
  <td align="right">
#if($items.HasFirst) $PaginationHelper.CreatePageLink( 1, "first" ) #end
#if(!$items.HasFirst) first #end
#if($items.HasPrevious) | $PaginationHelper.CreatePageLink( $items.PreviousIndex, "prev" ) #end
#if(!$items.HasPrevious) | prev #end
#if($items.HasNext) | $PaginationHelper.CreatePageLink( $items.NextIndex, "next" ) #end
#if(!$items.HasNext) | next #end
#if($items.HasLast) | $PaginationHelper.CreatePageLink( $items.LastIndex, "last" ) #end
#if(!$items.HasLast) | last #end
  </td>
  </tr>
</table>
</div>

This will result in something like the following

Cached Paging

The PaginationHelper has a cached version of its functionality. That allows the data source to be loaded and reused for a limited period of time. The cachekey must be carefully defined with the application semantics in mind. A bad cachekey might allow the data source to be shared by request from different users and with different data source search criterias (when the data source is the result of a search)

Suppose you have a form with many search criterias:

Your view will look something similar to this, and make sure to POST rather than GET:

<form id="search" method="post" action="$siteRoot/Cars/Search.aspx"
	<input id="make" type="text" size="14" name="make" /><br />
	<input id="model" type="text" size="14" name="model" /><br />
	<input id="year" type="text" size="14" name="year" /><br />
	<input type="Submit" class="button" value="Search!" />
</form>

The pagination helper exposes a delegate that you will need to grab your search results. Pass it your method that handles the actual searching. Here is an example controller:

public class CarController : SmartDispatcherController
{
	private String make, model, year;
	
	public IList PerformSearch()
	{
		// Do your actual search
		
		return Car.FindByMakeModelYear(make, model, year);
	}
	
	public void Search(String make, String model, String year)
	{
		// create the pagination
		
		PropertyBag["Cars"] = PaginationHelper.CreateCachedPagination(
			"carssearchkey" + this.make + this.model + this.year, 
			5, new 	DataObtentionDelegate(PerformSearch));
	}
}

Our resulting view, it is important that we expose our criteria to the view so the next/first/last/prev links can be correctly constructed and work properly:

<table cellpadding="4">
	<tr>
		<th>Make</th>
		<th>Model</th>
		<th>Year</th>
	</tr>
#foreach($Car in $Cars)
	<tr>
		<td>$!Car.Make</td>
		<td>$!Car.Model</td>
		<td>$!Car.Year</td>
	</tr>
#end
</table>
<div class="pagination" id="pagination">
<table width="90%" border="0">
	<tr>
		<td>Showing $Cars.FirstItem - $Cars.LastItem of $Cars.TotalItems</td>
		<td align="right">
		#if($Cars.HasFirst) 
			$PaginationHelper.CreatePageLink( 1, "first",null, "%{make=$make, model=$model,year=$year}" ) 
		#else
			first
		#end
		#if($Cars.HasPrevious) 
			| $PaginationHelper.CreatePageLink( $Cars.PreviousIndex, "prev",null, "%{make=$make,model=$model,year=$year}" ) 
		#else
			| prev 
		#end
		#if($Cars.HasNext) 
			| $PaginationHelper.CreatePageLink( $Cars.NextIndex, "next",null, "%{make=$make,model=$model,year=$year}" ) 
		#else
			| next
		#end
		#if($Cars.HasLast) 
			| $PaginationHelper.CreatePageLink( $Cars.LastIndex, "last",null, "%{make=$make,model=$model,year=$year}" ) 
		#else
			| last
		#end
		</td>
	</tr>
</table>
</div> <!-- end pagination -->

And how it looks:

13.3.7. WizardHelper

The WizardHelper is used in combination with MonoRail's Wizard support. It exposes useful methods to create dynamic wizard navigation.

13.3.8. TextHelper

The TextHelper provides methods for working with strings and grammar.

13.3.9. Effects2Helper

The Effects2Helper exposes the script.aculo.us script features.

13.3.10. DateFormatHelper

The DateFormatHelper is a simple helper to format DateTime instances.

Chapter 14. Resources and Localization

14.1. Introduction

You can associate one or more assembly resources with a controller. The resource entries will be available to your view as an dictionary. This is a good way to externalize static content.

In addition you can also use the LocalizationFilter to set up the CurrentCulture and CurrentUICulture on the thread. This makes .Net select the correct resource to load accordingly to the language set. It also defines how it should format numbers and dates.

Note

A sample demonstrating the concepts in the section can be downloaded from !download/monorail/trunk/MonoRail.LocalizationSample.zip

14.2. Using Resources

Resources can be associate with a controller using the ResourceAttribute . For example:

using Castle.MonoRail.Framework;

[Resource("text", "LocalizationSample.Resources.Home")]
public class HomeController : SmartDispatcherController
{
	...

The first parameter defines the key that you can use from your view. The second is the resource full name.

Note

There is a bug on Visual Studio that causes changes on the resource files to no be detected. If this happens, force a rebuild.

The entries can be accessed from the view as a regular dictionary:

<h2> $text.welcome </h2>

<p>
$text.intro
</p>

You can optionally set up the CultureName and the AssemblyName on the ResourceAttribute .

14.3. Setting Up the Current Culure

The LocalizationFilter allows you to define a strategy to extract the language code and sets up the CurrentCulture and CurrentUICulture for the request thread.

For example, it can extracts the locale from the headers the browser sends it, and allow overriding the locale using a cookie entry, or an entry in the session. For example:

using Castle.MonoRail.Framework;

[LocalizationFilter(RequestStore.Cookie, "locale")]
public class HomeController : SmartDispatcherController
{
	...

The usage above defines that it should look for a cookie named locale and in the case it cannot be found fallback to the browser locale.

14.4. Localization

Localization support is provided as a combination of both approaches.

using Castle.MonoRail.Framework;
using Castle.MonoRail.Framework.Filters;

[Resource("text", "LocalizationSample.Resources.Home")]
[LocalizationFilter(RequestStore.Cookie, "locale")]
public class HomeController : SmartDispatcherController
{
	public void Index()
	{
	}
	
	public void SetLanguage(String langCode)
	{
		Response.CreateCookie("locale", langCode);
		
		RedirectToAction("index");
	}
}

It is up to .net to load the correct resource or fallback to the default language if the resource for the specified locale cannot be found.

The SetLanguage action is an example of how to override the locale.

Chapter 15. Validation Support

Chapter 16. Sending Email

It is easy to send emails using MonoRail combining templates as you do with the views. Layouts are not applied to e-mail template, though.

Note

A sample demonstrating the concepts in the section can be downloaded from !download/monorail/trunk/MonoRail.SendingEmailSample.zip

First you have to configure the smtp host address. See TODO [[MonoRail Configuration Reference#Formal_reference|Formal reference topic]]

The most manual way to send an email is to configure a Castle.Components.Common.EmailSender.Message instance and invoke DeliverEmail which is exposed by the Controller class.

A better way is to externalize the configuration of the Message . You can do that by creating view templates on the mail folder under your views directory. You can even add headers like from, to and subject to the template. They will be used to properly set up the Message instance. The rest of the view template will be considered the e-mail body. If the body starts with an <html> tag, the Message format will be changed to Html.

The method RenderMailMessage can be used to create a configured Message instance based on the specified template. It gives you a chance to modify the message before sending it with DeliverEmail .

The method RenderEmailAndSend is a combination of the methods above. It creates the Message instance and sends it.

Chapter 17. Unit Testing

17.1. Introduction

Previously MonoRail used the ASP.Net infrastructure to run a web site and run tests against it. This worked for simple scenarios but didn't scale well or allow access to all aspects of the RailsEngineContext.

During RC2, BaseControllerTest was added to the trunk. BaseControllerTest exposed all of the properties of the MonoRail pipeline so that you can now inspect values as if running in a web context. This allowed us to mock the RailsEngineContext and inject mocks into the pipeline.

17.2. The TestSupport Assembly

Castle.MonoRail.TestSupport was created to enable easy testing of MonoRail projects. Classes in this namespace are for performing tests on MonoRail Controllers. It exposes the PropertyBag , Flash and Session dictionaries so you can write assert for their contents. You also have access to MockRailsEngineContext via the Context property to insert values to be used during the Controller execution.

In order to use the ASP.Net runtime for deprecated AbstractMRTestCase, the assembly Castle.MonoRail.TestSupport.dll must be registered in the GAC . If you have installed Castle using the MSI distribution this was already done for you. Otherwise, execute:

> gacutil /i Castle.MonoRail.TestSupport

17.3. Setting Up a Test Project

To set up a test project perform the following steps:

  1. Create a Class Library project (usually you are going to use the same solution of the web project)

  2. Add references to :

    • nunit.framework

    • Castle.MonoRail.Framework

    • Castle.MonoRail.TestSupport

  3. Create test cases class extending BaseControllerTest

  4. Call the PrepareController method passing Controller Instance, Area, Controller Name and Action Name.

  5. Finally make Assertion calls on PropertyBag or other parts of the Controller that is under test. You can access the RailsEngineContext from BaseControllerTest.Context

17.3.1. A simple example from MonoRail test case

The following class is a snippet of one of the MonoRail test cases:

[TestFixture]
public class BasicFunctionalityTestCase : BaseControllerTest
{
	[Test]
	public void SimpleControllerAction()
	{
		SimpleController simpleController = new SimpleController();
		PrepareController(simpleController, "areaName", "simplecontroller", "index");

		simpleController.Index();

		// Some Assertions here
	}

	[Test]
	public void Flash()
	{
		SimpleController simpleController = new SimpleController();
		PrepareController(simpleController, "areaName", "simplecontroller", "someotheraction");

		simpleController.SomeOtherAction();

		Assert.IsNotNull(simpleController.Flash["someothervalue"]);
	}

	[Test]
	public void Redirect()
	{
		SimpleController simpleController = new SimpleController();
		PrepareController(simpleController, "areaName", "simplecontroller", "redirectaction");

		simpleController.RedirectAction();

		Assert.IsNotNull("controller/action.rails", Response.RedirectedTo);
	}

	[Test]
	public void PropertyBag()
	{
		SimpleController simpleController = new SimpleController();
		PrepareController(simpleController, "areaName", "simpleController", "propertybagaction");

		simpleController.PropertyBagAction();

		Assert.IsNotNull(simpleController.PropertyBag["somevalue"]);
	}

	[Test]
	public void CurrentUser()
	{
		SimpleController simpleController = new SimpleController();
		PrepareController(simpleController, "areaName", "simpleController", "currentuseraction");

		simpleController.CurrentUserAction();

		Assert.IsNotNull(Context.CurrentUser);
	}
}

For for a more detailed example see TDDingControllers on using.castleproject.org For more documentation, please see the Castle.MonoRail.TestSupport namespace classes .

Chapter 18. Integrations

18.1. Introduction

Integrations are components that provide integration to other services in the Castle stack.

18.2. ActiveRecord

If you are using ActiveRecord you may consider using the integration we developed fo it. In order to do so, first of all, add a reference to the following assembly:

  • Castle.MonoRail.ActiveRecordSupport

18.2.1. The Main Players

ARSmartDispatcherController

What we are about to discuss only works if you are using ARSmartDispatcherController instead of SmartDispatcherController . The ARSmartDispatcherController offers a CustomBindObject method that is ActiveRecord-aware.

ARDataBindAttribute and ARDataBinder

So imagine that you are creating a CRUD page for a Customer object. Creation is really simple, and the DataBindAttribute attribute is enough:

public class CustomerController : ARSmartDispatcherController 
{
    public void Index()
    {
    }

    public void New()
    { 
    }

    public void Create([DataBind("customer")] Customer customer)
    { 
        try
        {
            customer.Create();

            RedirectToAction("index");
        }
        catch(Exception ex)
        {
            Flash["error"] = ex.Message;

            RedirectToAction("new", Request.Form);
        }
    }
}

Now editing is trick. You must load the Customer , and populate the changes with the form data. Enters ARDataBindAttribute :

public class CustomerController : ARSmartDispatcherController 
{
    ...

    public void Edit(int id)
    { 
        PropertyBag.Add("customer", Customer.Find(id));
    }

    public void Update([ARDataBind("customer", AutoLoad=AutoLoadBehavior.Always)] Customer customer)
    { 
        try
        {
            customer.Update();

            RedirectToAction("index");
        }
        catch(Exception ex)
        {
            Flash["error"] = ex.Message;

            RedirectToAction("edit", Request.Form);
        }
    }
}

The ARDataBindAttribute extends the DataBindAttribute so the Exclude and Allow properties are still there.

However, as you can see, we used AutoLoad=AutoLoadBehavior.Always . This tells the binder to collect the primary key value for the customer and load it, then populate the object. So all you have to do next is to invoke Save or Update method.

ARFetchAttribute

The ARFetchAttribute is a simpler version of ARDataBinder . It is in charge of loading the instance from the database and nothing more.

public class CustomerController : ARSmartDispatcherController 
{
    ...

    public void SetPassword([ARFetch("customer.id")] Customer customer, String newPassword)
    { 
        try
        {
            customer.Password = newPassword;
            customer.Save();

            RedirectToAction("index");
        }
        catch(Exception ex)
        {
            Flash["error"] = ex.Message;

            RedirectToAction("changepassword", Request.Form);
        }
    }
}

The optional parameter passed to ARFetch tells it which form field has the primary key value. If you don't specify it, it will use the parameter name (for the example above it would be customer )

You can also specify Required=true which will force it to throw an exception if the record is not found:

public class CustomerController : ARSmartDispatcherController 
{
    ...

    public void SetPassword([ARFetch("customer.id", Required=true)] Customer customer, String newPassword)
    { 
        ...
    }
}

And Create=true , which will create a new object if the primary key form field is empty:

public class CustomerController : ARSmartDispatcherController 
{
    ...

    public void CreateOrModifyCustomer([ARFetch("customer.id", Create=true)] Customer customer, String name, ...)
    { 
        customer.Name = name;
        customer.Save();
    }
}
The AutoLoad Property

It is very important that you know what the AutoLoad property means and the behavior it governs.

Enum fieldDescription
Never Means that no autoload should be performed on the target type or on nested types.
Always Means that autoload should be used for the target type and the nested types (if present). This demands that the primary key be present on the http request for the root type and nested.
OnlyNested Does not load the root type, but loads nested types if the primary key is present. If not present, sets null on nested type. This is useful for insertions.
NewInstanceIfInvalidKey Means that we should autoload, but if the key is invalid, like null , 0 or an empty string, then just create a new instance of the target type.
NullIfInvalidKey Means that we should autoload, but if the key is invalid, like null , 0 or an empty string, then just set null on the nested type.

18.2.2. DataBinding with ActiveRecord

DataBinding Issues

The combination of databinding and ActiveRecord opens the possibility for an error that is most often hit by unexperienced users, especially when using the recommended Session Per Request configuration for ActiveRecord. Consider the code from above:

public void Update([ARDataBind("customer", AutoLoad=AutoLoadBehavior.Always)] Customer customer)
{ 
    try
    {
        customer.Update();

        RedirectToAction("index");
    }
    catch(Exception ex)
    {
        Flash["error"] = ex.Message;

        RedirectToAction("edit", Request.Form);
    }
}	

Now, what happens if there is an exception? The exception is caught as intended and the error message added to the Flash container. But actually, you will get an ASP.NET errorpage nonetheless. The reason for this behaviour is simple once you know how the request is processed in the controller:

  1. An ActiveRecord SessionScope is created in OnBeginRequest()

  2. The customer object will be looked up from database by its primary key.

  3. The properties of the customer object are updated through databinding.

  4. customer.Update() is called and throws an exception.

  5. The catch block executes.

  6. The ActiveRecord SessionScope is disposed in OnEndRequest() :

    1. NHibernate checks whether there are pending changes.

    2. The customer object is marked dirty, because its property values have been changed during databinding.

    3. NHibernate flushes the session. During the flush, the changes of customer object are written back to the database.

    4. An unhandled exception occurs.

  7. The exception cannot be handled in your controller and an errorpage is shown.

Note

The same issue appears when using ActiveRecord's validation support. Behaviour and reasons are identical in that case: The object is not saved but changed by the DataBinder and will be saved by NHibernate when the session is disposed.

So, now that the problem is known, how can it be handled? There are plenty possible solutions for this, depending on the user's needs:

  1. Make the session readonly and always flush it explicitly.

  2. Remove the offending object from the session.

  3. Create a TransactionScope and roll it back.

  4. Use the Validate option of ARDataBind

Possible Solutions

Using a Read-Only Session

If you change the SessionScope to not automatically flush, changes are not flushed on disposal of the scope. You can setup the scope as shown below (based on this article ):

// GlobalApplication.cs
public void OnBeginRequest(object sender, EventArgs e)
{
    HttpContext.Current.Items.Add("nh.sessionscope", new SessionScope(FlushAction.Never);
}		

Doing so requires you to flush your sesssion manually in every controller that changes existing objects or introduces new objects to the database. There are two possibilities to get the session object to flush:

// Use this when the session was added to the HttpContext in OnBeginRequest
((SessionScope)Context.Items["nh.sessionscope"]).Flush();
// Gets the session, if it is not stored in a known place.
ActiveRecordMediator
    .GetSessionFactoryHolder()
    .CreateSession(typeof(Customer))
    .Flush();
Removing Invalid Objects From the Session

Another possibility is to keep the default behaviour and only assure that invalid objects are not flushed on disposal of the session. This strategy is recommended when there are objects that need to be saved to the database even when one object is invalid and must not be stored.

You might also want to use this strategy when you are using Windsor integration and the ActiveRecordIntegration facility, because in this case you cannot change the session to be read-only within your code.

To remove the conflicting object from the SessionScope , you must Evict it:

ActiveRecordMediator
    .GetSessionFactoryHolder()
    .CreateSession(typeof(Customer))
    .Evict(customer);

By using the ActiveRecordMediator , you will get access to the session regardless where it as been originally created.

Using TransactionScope

Another method is to wrap the validation in a transaction. If the validation fails, the transaction must be rolled back:

using (TransactionScope tx = new TransactionScope())
{
    try
    {
        customer.Update();
        RedirectToAction("index");
    }
    catch(Exception ex)
    {
        Flash["error"] = ex.Message;
		tx.Rollback();
        RedirectToAction("edit", Request.Form);
    }
}

You might wonder how this works because I wrote above that the DataBinder makes the offending changes outside of the TransactionScope . The answer is that TransactionScopes can be nested and that there is an implicit transaction started during the creation of the SessionScope . The rollback is then propagated to that transaction and all changes are discarded.

However, that means that if you have other objects that must be saved, you should instead evict the invalid object as descibed above

Validation During DataBinding

The most elegant method to circumvent such problems is the use of the Castle Validation component. By setting the parameter Validate of the ARDataBind -attribute to true , MonoRail performs validation of the input data before changing the properties based on the Validate XXX -Attributes of the ActiveRecord model classes.

The drawback is that invalid data is completely discarded and will not redisplayed to the client by FormHelper . In order to access the data, Request.Params must be used.

public void Update(
    [ARDataBind("customer", 
        AutoLoadBehavior.NewRootInstanceIfInvalidKey, 
        Validate = true)] Customer customer)
{
    if (ValidationSummaryPerInstance[info].ErrorsCount > 0)
    {
        string msg = "Please correct errors:";
        foreach (string p in ValidationSummaryPerInstance[customer].InvalidProperties)
        {
            msg += "<p>" + p + ":</p>";
            foreach (string m in ValidationSummaryPerInstance[customer].GetErrorsForProperty(p))
            {
                msg += string.Format("<p>{0}</p>", m);
            }
        }
        Flash["message"] = msg;
        Flash["customer"] = customer;
        RedirectToReferrer();
        return;
    }
	// "Normal" application flow
	// ...
}

18.3. ActiveRecord Scaffolding

This support still on its early stages and has been refactored twice. Nevertheless it's quite good to create prototype applications in no time. Its implementation allow you to override the views if you want, so the scaffolding will handle only the CRUD.

The scaffolding support basically relies on Dynamic Actions to do its magic. It adds dynamic actions to your controller based on the name of the target ActiveRecord class. Some of the virtual actions might use a view, and if so you are allowed to provide your own views instead of letting it generate the html for you.

Here is a list of the actions added dynamically:

  • new: Presents a form to the user fill in order to create the item on the database

  • create: Takes the information submited by the newAccount and creates the item

  • edit: Presents a form to the user fill in order to update the item on the database

  • update: Takes the information submited by the editAccount and changes the item

  • list: Presents a paginated list of items saved

  • confirm: Asks the user if he/she confirms the removal of the item

  • remove: Attempt to remove the item and presents the results

If more than one ScaffoldingAttribute is associate with a controller, then the actions will have the entity name:

  • new<typename>: Presents a form to the user fill in order to create the item on the database

  • create<typename>: Takes the information submited by the newAccount and creates the item

  • edit<typename>: Presents a form to the user fill in order to update the item on the database

  • update<typename>: Takes the information submited by the editAccount and changes the item

  • list<typename>: Presents a paginated list of items saved

  • confirm<typename>: Asks the user if he/she confirms the removal of the item

  • remove<typename>: Attempt to remove the item and presents the results

18.3.1. Required Assemblies

The required assemblies are:

  • Castle.Components.Common.TemplateEngine

  • Castle.Components.Common.TemplateEngine.NVelocityTemplateEngine

  • Castle.MonoRail.ActiveRecordScaffold

  • Castle.MonoRail.ActiveRecordSupport

  • NVelocity

And the ActiveRecord assemblies:

  • Castle.ActiveRecord

  • Iesi.Collections

  • NHibernate

  • log4net

There is no configuration beyond that of ActiveRecord required. For further information on configuring ActiveRecord see the ActiveRecord documentation.

18.3.2. ScaffoldingAttribute

Now you can create a controller (or use an existing one) and add the ScaffoldingAttribute pointing to the an ActiveRecord class. Suppose you have the following an ActiveRecord class:

[ActiveRecord("Blogs")]
public class Blog : ActiveRecordValidationBase
{
    private int _id;
    private String _name;
    private String _author;

    [PrimaryKey(PrimaryKeyType.Native)]
    public int Id
    {
        get { return _id; }
        set { _id = value; }
    }

    [Property, ValidateNotEmptyAttribute]
    public String Name
    {
        get { return _name; }
        set { _name = value; }
    }

    [Property, ValidateNotEmptyAttribute]
    public String Author
    {
        get { return _author; }
        set { _author = value; }
    }

    public static void DeleteAll()
    {
        ActiveRecordBase.DeleteAll( typeof(Blog) );
    }

    public static Blog[] FindAll()
    {
        return (Blog[]) ActiveRecordBase.FindAll( typeof(Blog) );
    }

    public static Blog Find(int id)
    {
        return (Blog) ActiveRecordBase.FindByPrimaryKey( typeof(Blog), id );
    }
}

You can then create a BlogsController like this:

[Scaffolding( typeof(Blog) )]
public class BlogsController : Controller
{
}

Now point your browser to your controller, and to an action called list.rails like

http://localhost/blogs/list.rails

18.3.3. List

The image below is the default list rendered if you have used the stylesheet and layout mentioned before:

If you want to supply your own view, add a file name list<typename>.vm on the view folder for the controller. The following data will be available to your view on the PropertyBag :

  • items: a paginated list of items

  • model: The ActiveRecordModel instance for the target type

  • keyprop: A PropertyInfo of the property that is the primary key

  • properties: A list of PropertyInfo

18.3.4. Add

The image below is the default new entry page rendered if you have used the stylesheet and layout mentioned before:

If you want to supply your own view, add a file name new<typename>.vm on the view folder for the controller. The following data will be available to your view on the PropertyBag :

  • instance: A newly created instance of the target type

  • armodel: The ActiveRecordModel instance for the target type

18.3.5. Edit

The image below is the default edit entry page rendered if you have used the stylesheet and layout mentioned before:

If you want to supply your own view, add a file name edit<typename>.vm on the view folder for the controller. The following data will be available to your view on the PropertyBag :

  • instance: A newly created instance of the target type

  • armodel: The ActiveRecordModel instance for the target type

18.3.6. Remove

The default rendered page will ask for confirmation on the removal. If you want to supply your own view, add a file name confirm<typename>.vm on the view folder for the controller. The following data will be available to your view on the PropertyBag :

  • instance: A newly created instance of the target type

  • armodel: The ActiveRecordModel instance for the target type

  • id: The value of the primary key that identifies the record

18.3.7. Complex Models

Complex models should be supported. For example, the following is a page rendered for a Person type that uses inheritance and nested types (what NHIbernate calls Components)

18.4. Windsor Container

By enabling Windsor Container integration, your controllers, filters and ViewComponents might request dependencies or configurations that can be satisfied by the container, thus leading your design to a loosely coupled state.

Warning

Once the integration is set you must register controllers and ViewComponents as ordinary container components. Filters can optionally be registered. To look up a ViewComponent on the view you must use the key used to register the component on the container instead of the ViewComponent name.

18.4.1. Benefits

Once the integration is set you can take advantage of all the benefits offered by an Inversion of control container.

Suppose you have a controller that receives files uploads. Where should it store the files? Make it configurable.

On the controller:

public class ImageGalleryController : Controller
{
	...

	public ImageGalleryController(String imageDirectory)
	{
		this.imageDirectory = imageDirectory;
	}

	...
}

Let's assume that this controller was registered on the container with the key imagegallery.controller. On the configuration section:

<castle>

  <components>

	<component id="imagegallery.controller">
	  <parameters>
		<imageDirectory>C:\mytempdir\safedir</imageDirectory>
	  </parameters>
	</component>

  <components>

</castle>

Your controller could assume a default directory and allow it to be overriden as well:

public class ImageGalleryController : Controller
{
	private String imageDirectory = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData);

	public ImageGalleryController()
	{
	}

	public string ImageDirectory
	{
		get { return imageDirectory; }
		set { imageDirectory = value; }
	}

	...
}

The previous configuration for the component still valid. But now it is optional as the controller can live without it.

18.4.2. Required Assemblies

Make your web project reference the following assemblies:

  • Castle.DynamicProxy.dll

  • Castle.Model.dll

  • Castle.MicroKernel.dll

  • Castle.Windsor.dll

  • Castle.MonoRail.WindsorExtension.dll

18.4.3. Configuration

Use the useWindsorIntegration attribute:

<?xml version="1.0" ?> 
<configuration>

    <configSections>
        <section name="monorail"
                 type="Castle.MonoRail.Engine.Configuration.MonoRailSectionHandler, Castle.MonoRail.Engine" />
        <section name="castle"
                 type="Castle.Windsor.Configuration.AppDomain.CastleSectionHandler, Castle.Windsor" />
    </configSections>

    <monorail useWindsorIntegration="true">
		<viewEngine 
			viewPathRoot="views" 
			customEngine="Castle.MonoRail.Framework.Views.NVelocity.NVelocityViewEngine, Castle.MonoRail.Framework.Views.NVelocity" />
    </monorail>

    <castle>

      <!-- component and facilities configuration goes here -->

    </castle>

</configuration>

18.4.4. Exposing the Container

You must make your container available to the web application. The best place for it is the global.asax:

<%@ Application Inherits="YourApp.Web.MyGlobalApplication"  %>

MyGlobalApplication.cs:

namespace YourApp.Web
{
    using System;
    using System.Web;

    using Castle.Windsor;
    using Castle.ActiveRecord;


    public class MyGlobalApplication : HttpApplication, IContainerAccessor
    {
        private static WebAppContainer container;

        public void Application_OnStart()
        {
            container = new WebAppContainer();
        }

        public void Application_OnEnd() 
        {
            container.Dispose();
        }

        public IWindsorContainer Container
        {
            get { return container; }
        }
    }
}

18.4.5. Initializing

You must register a facility named RailsFacility. It ensures that the lifestyle of each controller is set to Transient - as using the default, singleton, would be a terrible mistake - and register each component/controller on the ControllerTree .

Note

Also note that from now on, your controllers, view components and optionally the filters are standard components, and they need to be registered on the container.

namespace YourApp.Web
{
	using System;

	using YourApp.Web.Controllers;

	using Castle.Model.Resource;
	using Castle.Windsor.Configuration.Interpreters;
	using Castle.MonoRail.WindsorExtension;

	public class WebAppContainer : WindsorContainer
	{
		public WebAppContainer() : base(new XmlInterpreter( new ConfigResource() ))
		{
			RegisterFacilities();
			RegisterComponents();
		}

		protected void RegisterFacilities()
		{
			AddFacility( "rails", new RailsFacility() );
		}

		protected void RegisterComponents()
		{
			AddComponent( "home.controller", typeof(HomeController) );
		}
	}
}

Obviously you can decide to register the facility and the components directly on the configuration file. The approach above is just a suggestion.

Chapter 19. Advanced Topics

19.1. Introduction

This chapter covers a series of advanced topics.

19.2. Security

There are a few security related issues you should consider when configuring your MonoRail application.

First if your view directory is in the web folder then clients can potentially see the source code of the views, which can expose potentially sensitive information to parties you would prefer not to have access to it. To prevent this, associate the view extension with an IHttpHandler that comes with ASP.Net.

Second, if you use the DataBinder to populate classes, you might want to provide an Exclude or Allow list to prevent people from populating properties that are not on the form. Check the DataBind documentation for more information.

19.3. Routing

MonoRail supports simple URL rewrites based on regular expressions. However, to use it for nicer URLs you must allow the ASP.NET ISAPI to handle all file extensions, which has a performance penalty.

Another approach is to use an ISAPI filter that is able to rewrite URLs based on pattern matching. Mod_Rewrite is one example of an ISAPI filter which performs URL rewriting. By using a ISAPI filter to rewrite URLs no wildcard mapping needs to be configured to direct all requests to the ASP.NET ISAPI. Even though the ASP.NET ISAPI will process requests for static files this is not ideal for a production site because of the performance penalty.

Other URL rewriting solutions are available by different companies, some of them are free. ISAPI_Rewrite Lite is one example.

19.3.1. Configuration

1 - Depending on how you intend to use routing will determine how it needs to be configured. If you will always be using filenames you only need map the specific extensions you want to work with (such as .aspx, .rails, .content, .article, etc). However, if you intend on routing directories (such as http://localhost/myapp/somedir/) then you will need to map everything (*.*) in IIS to aspnet_isapi.dll which causes IIS to redirect every request to the ASP.NET ISAPI (be aware of the consequences).

2 - Create a routing element under the monorail element in your web.config as shown below. Routing rules will be evaluated in a top-down order until a match is found. If there are no matches the request will continue as normal with the requested URL.

<monorail>
	<routing>
		<rule>
			<pattern>(/blog/posts/)(\d+)/(\d+)/(.)*$</pattern>
			<replace><![CDATA[ /blog/view.rails?year=$2&month=$3 ]]]]></replace>
		</rule>
		<rule>
			<pattern>(/news/)(\d+)/(\d+)/(.)*$</pattern>
			<replace><![CDATA[ /news/view.rails?year=$2&month=$3 ]]]]></replace>
		</rule>
	</routing>
</monorail>

3 - Add the routing module to the httpModules element of system.web in your web.config. Ensure that the routing module is listed before the monorail module as shown below.

	<system.web>
		<httpHandlers>
			<add verb="*" path="*.rails"
			  type="Castle.MonoRail.Engine.MonoRailHttpHandlerFactory, Castle.MonoRail.Engine" />
		</httpHandlers>
		
		<httpModules>
			<!-- NOTE: Routing MUST come before the monorail module -->
			<add name="routing" type="Castle.MonoRail.Framework.RoutingModule, Castle.MonoRail.Framework" />
			<add name="monorail" type="Castle.MonoRail.Framework.EngineContextModule, Castle.MonoRail.Framework" />
		</httpModules>
	</system.web>

The regular expressions are compiled, therefore performance should be acceptable. If no matches are found then the request is processed as it would be without routing.

This example routing rule defines that a request for the URL /blog/posts/2000/11/anything will be processed as if it was /blog/view.rails?year=2000&month=11 :

<pattern>(/blog/posts/)(\d+)/(\d+)/(.)*$</pattern>
<replace><![CDATA[ /blog/view.rails?year=$2&month=$3 ]]></replace>

19.3.2. Root Directory Mapping Workaround

If you do not want to setup a wildcard mapping just to get a default document for your root directory you can use these steps:

  • Create an empty file in your root directory that is mapped to aspnet_isapi.dll and is a configured as a default document in IIS. For example, create a file named index.rails or default.aspx depending on your configuration.

  • Then create a new routing rule in your web.config as shown below.

	<monorail>
		<routing>
			<rule>
				<pattern>^(/index.rails)(.)*$</pattern>
				<replace><![CDATA[ /Controller/Action.rails?$2 ]]></replace>
			</rule>
		</routing>
	</monorail>

Note

This work around is only feasible for root directories because the default document file must be created in each directory which is not practical for URLs that contain dynamic strings.

19.3.3. Another Approach

David Moore has sent an interesting approach he uses:

"I really like mod_rewrite, but as you can see it requires 3rd party components also.

The easiest thing is to use what you already have at your fingertips with IIS, ASP.NET and MonoRail itself, and route all requests through ASP.NET. Then, you can use the MonoRail URL rewriting features within web.config.

Now performance-wise, you don't want to route requests through the ASP.NET/MonoRail stack for things such as .css files, image resources etc. What I do for this is that I map the URLs for all static resources to another virtual directory which is purely IIS serving up these files.

I.e. I make the $static variable available in all my NVelocity templates, and if I insert an image it looks like this: <img src="$static/images/image.gif"/> as a simple example.

This currently resolves to http://localhost/static/images/image.gif, with /static being a virtual directory, and within there all my static content (css, javascript, images etc).

This is very scalable in that in a production environment, we can change our $static to point to http://static.ourdomain.com and have light and fast web servers such as lighttpd or just a vanilla IIS serving up our static content fast, easing the load on a dedicated application server.

So in summary, I evaluated mod_rewrite-style alternatives and went with the simple solution built into MonoRail, and implemented a good practice of having a separate URL for static content which is going to make our application more scalable in the future anyway."

19.3.4. Additional Information

Josh wrote a very interesting post about MonoRail and Url rewriting .

Colin Ramsay wrote about rewriting options for IIS .

19.4. Caching Support

19.5. Transformation Filters

Transformation Filters allow you to manipulate the stream of data that defines a generated page before it is sent to the client.

Right before a generated page is streamed to the client, you can use a Transformation Filter to manipulate the stream. The Transformation Filter is applied after the view engine has built the page. Your view engine is not used with a Transformation Filter.

To assign a Transformation Filter to an action, use the TransformationFilter attribute.

public class UserController : Controller
{
	[TransformationFilter(typeof(UpperCaseTransformationFilter))]
    public void View()
    {
    }
}

In the example above, the entire page generated by the View action will be converted to upper case text.

You may apply multiple Transformation Filters to an action. To control their order of execution, use the ExecutionOrder property.

public class UserController : Controller
{
	[TransformFilter(typeof(WikiTransformFilter), ExecutionOrder=1), TransformFilter(typeof(UpperCaseTransformFilter), ExecutionOrder=2)]
	public void View()
    {
    }
}

In the example above, the page generated by the View action will first be transformed using the WikiTransformFilter and then the result of that will be transformed to upper case text.

You can create your own Transformation Filters by inheriting from the TransformFilter abstract base class.

19.6. Dynamic Actions

Dynamic actions and action providers are a way to create custom and dynamic functionality.

Note

A sample demonstrating the concepts in the section can be downloaded from !download/monorail/trunk/MonoRail.DynActProvSample.zip

MonoRail considers every public instance method of a controller an action, for certain situations this is not enough. For example, you want to decide on the name for a section in the URL at runtime when the data is available; or a set of controllers that inherit from a distinct common super class need the same action which can be defined in the super class.

Dynamic Actions are a way to associate code with a name in runtime.

A dynamic action is nothing more than an implementation of the interface IDynamicAction :

public interface IDynamicAction
{
    /// <summary>
    /// Implementors should perform the action 
    /// upon this invocation
    /// </summary>
    void Execute(Controller controller);
}

You can associate a dynamic action with a controller using the DynamicActions property:

public class MyController : Controller
{
    public MyController
    {
        DynamicActions["index"] = new IndexDynamicAction();
    }
}

19.6.1. Dynamic Action Providers

Dynamic Action Providers are in charge of adding dynamic actions to controllers. They can be associated with controllers using the DynamicActionAttribute :

[DynamicAction(typeof(MyDynActionProvider))]
public class MyController : Controller
{
    public MyController
    {
    }
}

The implementation of the provider can be something static (ie. always add the same set of actions) or can "read" something from the controller or the logged user; and add the actions accordingly:

public class MyDynActionProvider : IDynamicActionProvider
{
    public void IncludeActions(Controller controller)
    {
        controller.DynamicActions["index"] = new IndexDynamicAction();
    }
}

19.7. Scaffolding

Scaffolding is a idea borrowed from Ruby on Rails . It refers to the ability to create pages with a simple interface to data in a database with very little effort.

As every project under the Castle Project umbrella does not obligate you to embrace it all, scaffolding is implemented with the IScaffoldingSupport interface.

MonoRail will instantiate the implementation specificed if it discovers a controller with one or more ScaffoldingAttribute :

[Scaffolding(typeof(Blog))]
public class BlogsController : Controller
{
    public BlogsController()
    {
    }
}

The scaffolding implementor should register actions as a dynamic action provider.

The default implementation of scaffolding support relies on Castle ActiveRecord and is discussed in the integrations documentation section .

19.8. Extensions

From MonoRail beta 5 you can use the new Extensions support which allow you to plug existing extensions or develop your own extensions. This document should clarify why extensions were introduced, a list of out-of-box extensions and how to create your own extensions.

The user and development community on MonoRail is quite active, and consequently we receive lots of suggestion about new interesting features. Some of the features are unarguably helpful for the almost every user, however most of them are only useful to some specific users/scenarios. So implementing the new feature directly into the core framework would not make much sense as they usually increase the request flow overhead and the users that are not using the feature would be penalized.

Extensions were introduced to allow the framework to be extended easily and extensions to be reused.

19.8.1. Creating Custom Extensions

Creating an extension is a fairly simple task. Just create a new public class and implement the IMonoRailExtension or extend from AbstractMonoRailExtension. The latter is simpler.

After that you can override the methods you want. Usually your extension will read some custom configuration from the MonoRailConfiguration to configure itself if necessary and then perform some action when one of the hook methods is invoked.

MonoRail must be told that an extension exists. You can do that by using the extensions node in the configuration file. For more on that, check the MonoRail Configuration Reference document.

19.8.2. Built In Extensions

With the beta 5 version we shipped two extensions. As they can be helpful for some scenarios, they were also developed to serve as sample extensions, so the code is very simple.

Custom Session Extension

The Custom Session extension allow you to plug a custom implementation for the session available on IRailsEngineContext . This can be useful if you have requirements to implement on a session that ASP.Net Session strategies can not fulfil.

In order to use this extension you must provide an implementation of ICustomSessionFactory which would be responsible to create your own session implementation. A really naive implementation could map some cookie value to an instance of Hashtable , for instance.

Extensions were introduced to allow the framework to be extended easily and extensions to be reused.

You need to install the extension using the extensions node, as usual, and also provide the attribute customsession to inform the type that implements ICustomSessionFactory as follows:


<monorail customsession="Type name that implements ICustomSessionFactory">
	<extensions>
		<extension 
			type="Castle.MonoRail.Framework.Extensions.Session.CustomSessionExtension, Castle.MonoRail.Framework" />
	</extensions>
</monorail>

Exception Chaining Extension

The Exception Chaining extension allow you to execute one or more steps in response to an exception threw by an action. The steps are called Exception Handlers and must implement IExceptionHandler (or IConfigurableHandler if they need external configuration).

The IExceptionHandler interface is very straighforward, it simply dictates the contract for processing the exception information. As they are chained you must be good and check if there's a next handler available and if so, delegate the invocation to it. It would be also a good behavior if your handler implementation doesn't throw exceptions at all.

It is also important to note that you can use the AbstractExceptionHandler to save you some few types.

The IConfigurableHandler is just an increment the the previous interface for those handlers that require configuration information. The Configure method is invoked as soon as the handler is instantiated and its node on the configuration is passed.

The extension does not do much more than delegating the execution to the installed handlers. You can create handlers to provide actions and even introduce new semantics. As the handlers are chained together, you can even implement a handler that decides if the execution chain should continue or stop right there. For example, suppose you want that only exceptions that extends SqlException be e-mailed to you. In this case you could write this simple handler:

public class MyFilterHandler : AbstractExceptionHandler
{
	public override void Process(IRailsEngineContext context, IServiceProvider serviceProvider)
	{
		if (context.LastException is SqlException)
		{
			InvokeNext(context, serviceProvider);
		}
	}
}

And of course, register this handler before others.

The EmailHandler

MonoRail comes with just one exception handler: EmailHandler . This handler e-mails the exception details and the environment details to a specified e-mail address.

This handler requires the attribute mailto and you can optionaly inform the mailfrom as well. Also, you must provide the smtpHost in the configuration -- see MonoRail Configuration Reference for more details on this one.

<monorail smtpHost="my.smtp.server">
	<extensions>
		<extension 
		type="Castle.MonoRail.Framework.Extensions.ExceptionChaining.ExceptionChainingExtension, Castle.MonoRail.Framework" />
	</extensions>
	<exception>
		<exceptionHandler 
			mailTo="lazydeveloper@mycompany.com" mailFrom="angry.client@client.com" 
			type="Castle.MonoRail.Framework.Extensions.ExceptionChaining.EmailHandler, Castle.MonoRail.Framework" />
	</exception>
</monorail>

You need to install the extension using the extensions node, as usual, and also provide the node exception to list the handlers you want to install. Please note that they will be installed and chained in the same order they were declared.

<monorail customsession="Type name that implements ICustomSessionFactory">
	<extensions>
		<extension 
			type="Castle.MonoRail.Framework.Extensions.ExceptionChaining.ExceptionChainingExtension, Castle.MonoRail.Framework" />
	</extensions>
	<exception>
		<exceptionHandler type="type that implements IExceptionHandler" />
	</exception>
</monorail>

19.9. Service Architecture

It's impossible to come up with a sophisticated software where the default behavior pleases everyone and integrates with everything. The usual solution is making the software rely on contracts and having the core code as just a coordination of invocations on the contracts implementation. An user is thus capable of replacing one or more contract implementation.

The challenging is implementing an architecture where the parts are easily replaced, configurable and can rely (depend) on other parts.

MonoRail uses a set of services to handle specific tasks. The framework is responsible for defining the default implementations, instantiate, start and configure them. The services are made available through a combination of lifecycle interfaces and an implementation of IServiceProvider

The most usual solution to this problem is to use an Inversion of Control container. However, things have to be balanced. For MonoRail, an IoC container would introduce dependencies on assemblies and an longer initialization. In the end, we wouldn't benefit from all IoC container features, so it could be considered too much for our problem.

Instead, we combined what is already on the .Net library and some creative solution.

Basically we create two levels of services registries, per application and per request, and a simple interfaces that defines lifecycles that services optionally implement. This allow the service to start its work when it is supposed to and to gather reference to other services.

19.9.1. How It Works

When the web application is started, the ASP.Net modules are initialized. MonoRail has a EngineContextModule which is in charge of

  • Read the configuration

  • Initialize the services

  • Subscribe to ASP.Net application and request level events

  • Create a request context (which we'll not cover here)

Services implementation can be defined in the configuration section. After reading the configuration section MonoRail checks for missing definition and register the missing services using the default implementation.

After that it instantiates every service and runs the lifecycle. If everything went well, the framework is properly initialized. All services are registered in the application level container, which happens to be implemented by the EngineContextModule class. We call this the parent container.

When a request starts, MonoRail creates a DefaultRailsEngineContext instance, which also is a container for services. MonoRail sets the parent container on it. This allow the user to override services per request, and resolution of services in the parent.

  1. The configuration is read into the MonoRailConfiguration . It's also registered as a service

  2. The services collected are instantiated and registered

  3. The lifecycle is executed

19.9.2. Lifecycle Interfaces

A service might implement a few interfaces to expose to MonoRail that it behaves in a specific way or that it needs something from the framework.

ISupportInitialize (from System.ComponentModel)

This interface can be implemented if a service wants performs some initialization.

IInitializable (from Castle.Core)

This interface can be implemented if a service wants performs some initialization.

IServiceEnabledComponent (from Castle.Core)

This interface can be implemented if a service uses other services.

IServiceEnabledComponent is the first one to be invoked. This gives a chance to services gather all services references it wants. Then the initialization interface's methods are invoked.

For service that uses IServiceEnabledComponent lifecycle, the implementor should keep in mind that the initialization lifecycle has not run for all services, so it might not be safe to use other services as they might not be properly initialized at the moment.

The order of service registration and instantiation is not guaranted. So the implementor should not make any assumption regarding it.

19.9.3. Registering Services

You have to use the monorail configuration node to override or add services to MonoRail.

19.9.4. Built-In Services

The following is a succint list of services and their roles. You can refer to this list to learn more about MonoRail inner workings or to use them when developing extensions and new services.

MonoRailConfiguration

Exposes the MonoRail configuration

ExtensionManager

Manages registered extensions dispatching events from Asp.Net infrastructure and from MonoRail services

IViewSourceLoader

Sits in front of the file system and from assembly resources. It is used by view engines to obtain view streams

IViewEngine

Process view templates

IScaffoldingSupport

Adds scaffold support to a controller

IControllerFactory

Creates the controller instances

IViewComponentFactory

Manages registered ViewComponents and creates their instances

IFilterFactory

Manages registered filters and creates their instances

IResourceFactory

Create resources

IEmailSender

Sends e-mail

IEmailTemplateService

Process e-mail templates using the MonoRail infrastructure

IControllerDescriptorProvider

Inspects Controller types building a descriptor of what has been defined using attributes

IResourceDescriptorProvider

Creates descriptors for resources declared on controllers

IRescueDescriptorProvider

Creates descriptors for rescues declared on controllers

ILayoutDescriptorProvider

Creates descriptors for layouts declared on controllers

IHelperDescriptorProvider

Creates descriptors for helpers declared on controllers

IFilterDescriptorProvider

Creates descriptors for filters declared on controllers

IControllerTree

Manages a binary tree of controllers registered

ICacheProvider

Manages the cache

19.10. Custom Bindable Parameters

When an action is invoked on a controller that extends SmartDispatcherController , it inspects the parameters for attributes that implement IParameterBinder . The DataBindAttribute for exemple is one of these attributes.

This allow you to create binding logic, or add validation or anything that you want. You just need to create an attribute that applies to method arguments and implements the IParameterBinder

19.11. Using Resources to Store Views

Aside from the file system you can have views on the resource assemblies. This feature was introduced due to a necessity to reuse controllers, filters and views among projects, for example an user administration module.

To configure the assemblies that have views, use the additionalSources configuration node. See MonoRail Configuration Reference document.

19.12. Enabling Logging

You can supply an implementation of ILoggingFactory so MonoRail can use logger for its internal pieces and allow you to use the Controller.Logger .

Use the services node with the key Custom . The example below uses log4net. Make sure you have a log4net.config on the root directory that configures log4net.

<monorail>
	
	<services>
		<service 
			id="Custom" 
			interface="Castle.Core.Logging.ILoggerFactory, Castle.Core"
			type="Castle.Services.Logging.Log4netIntegration.Log4netFactory, Castle.Services.Logging.Log4netIntegration" />
	</services>
	
	...