Table of Contents
MonoRailConfigurationExtensionManagerIViewSourceLoaderIViewEngineIScaffoldingSupportIControllerFactoryIViewComponentFactoryIFilterFactoryIResourceFactoryIEmailSenderIEmailTemplateServiceIControllerDescriptorProviderIResourceDescriptorProviderIRescueDescriptorProviderILayoutDescriptorProviderIHelperDescriptorProviderIFilterDescriptorProviderIControllerTreeICacheProviderTable of Contents
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.
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.
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 .

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

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

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

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

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

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

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

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


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


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

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

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

Right-click the
Default Web Site
item and choose
Select the
Home Directory
tab
Click


Click .
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).
Fill in the extension (for example
.rails
) as the file extension (make sure you do not
omit the leading dot).
Uncheck the Check file exists check box.
Windows XP Pro users : If your 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 button.

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.
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.
We assume that you have Apache Httpd, mod_mono and xsp from the Mono Project installed.
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.
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
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>
Table of Contents
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.
The section
monorail
must be declared on the
configSections
.
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&month=$3 ]]]]></replace> </rule> <rule> <pattern>(/news/)(\d+)/(\d+)/(.)*$</pattern> <replace><![CDATA[ /news/view.rails?year=$2&month=$3 ]]]]></replace> </rule> </routing> </monorail>
| Attribute | Description |
|---|---|
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) |
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.
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.
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.
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.
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
.
| Attribute | Description |
|---|---|
viewPathRoot
| The folder that contains the views |
customEngine
|
Full .net type name of a type that
implements
IViewEngine
|
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.
| Attribute | Description |
|---|---|
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. |
MonoRail is composed of a few services. They can be replaced by custom implementations.
| Attribute | Description |
|---|---|
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. |
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.
Table of Contents
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.
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.
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
We refer to action the procedures that can be invoked on your controller. Basically it translates to any public instance method your controller exposes.
If you do not want that a specific method be "invocable", it cannot be public.
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");
}
}
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:
| Name | Description |
|---|---|
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). |
Request/Response
| Property | Type | Description |
|---|---|---|
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
| Property | Type | Description |
|---|---|---|
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
| Property | Type | Description |
|---|---|---|
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. |
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.
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.
Since the RC2 release empty strings are converted to
null
strings.
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)
{
...
}
}
Nullables data types are also supported. They will only be populated if the values are present on the form and in non-empty fields.
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)
{
...
}
}
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)
{
}
}
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; }
}
}
Your class must have a default parameterless constructor.
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.
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 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
}
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)
{
...
}
}
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 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);
}
...
}
}
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");
...
}
...
}
}
The following types are natively supported by the
DataBinder
component:
| Type name | Note |
|---|---|
| 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. |
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.
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:

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.
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.
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:
| Method | Description |
|---|---|
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
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.
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
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]]
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) ]
};
}
...
Table of Contents
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.
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.

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.
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
.
The view is not processed when
RenderView
is invoked. It just selects the view. The view process
happens after the action method returns.
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.
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:
The user access the action
PasswordManagement
.
A page with some action appears, including a
change password form that posts to the
ChangePassword
action.
On the
ChangePassword
action we perform a naive check and, in the
event of failure, add an entry to the Flash.
We send the user back to the
PasswordManagement
action sending a redirect.
The view for the
PasswordManagement
needs to check the flash entry error and
show a meaningful error message accordingly.
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
.
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();
}
}
Each view engine accesses the values in the PropertyBag and Flash differently. See the ViewEngines section for further information.
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) { ... }Table of Contents
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.
A sample demonstrating the concepts in the section can be downloaded from !download/monorail/trunk/MonoRail.ViewComponentSample.zip
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();
}
}
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
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.
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> </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:

MonoRail comes with a few ViewComponents for specific tasks. The documents on this area depicts their usage.
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
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
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.
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:
A sample demonstrating the concepts in the section can be downloaded from !download/monorail/trunk/MonoRail.DiggStylePagination.zip
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:

When creating a non-english application, the labels
« prev
and
next »
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
«
#end
#next
»
#end
#end
</td>
</tr>
You can see the results below. Pay attention to encircled links.

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

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">« 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> …<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 »</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:

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.
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.
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> — <a href="index8.rails?desc=true">Descending list</a>
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.
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>

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)); } } }
Table of Contents
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.
| Engine | Language | Compiled | Helpers | ViewComponents |
|---|---|---|---|---|
| WebForms | Any .net language | Yes | Yes | No |
| NVelocity | Velocity | No | Yes | Yes |
| Brail | boo | Yes | Yes | Yes |
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" />
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.
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>
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.
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.
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.
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
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 Variable | Description |
|---|---|
| $controller | The controller being executed. |
| $context | The IRailsEngineContext. |
| $request | context.Request |
| $response | context.Response |
| $session | context.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. |
| $siteroot | context.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
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
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.
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"];
...
}
}
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.
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}")
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();
}
}
}
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.
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.
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.
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.
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.
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"/>
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.
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 -----
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.
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: | |
|---|---|---|---|
| debug | Generate debug or retail code | false |
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. | none | Any valid namespace |
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.
The code separator types cannot be mixed. Only one type of separators must be used per file.
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.
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.
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.
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()}
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.
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.
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.
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
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"] }
%>
Using layouts is very easy, it is just a normal script that outputs ChildOutput property somewhere, here is an example:
Header
${ChildOutput}
Footer
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.
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.
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.
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.
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 %>
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.
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.
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.
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.)
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
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" />
Table of Contents
Filters are executed before and|or after your actions. It is useful for security, dynamic content and to keep away repetitive code.
To create a filter, create a class that implements the
IFilter
interface, then associate the filter with your controller.
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()
{
}
}
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
.
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
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()
{
}
}
Table of Contents
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 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.
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.
Table of Contents
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.
A sample demonstrating the concepts in the section can be downloaded from !download/monorail/trunk/MonoRail.SendingEmailSample.zip
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.
Table of Contents
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.
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:

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:

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.
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.
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.
Table of Contents
Helpers are associated with a controller and made available to be used on the view. They are usually used to reuse some generation code.
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
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.
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.
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
.
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 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>
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.
FormHelper is able to generate single and multi-value selects. Make sure you have read the How FormHelper works with sets document.
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 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.
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>
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.
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.
| Parameter | Definition |
|---|---|
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. |
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>
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()
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
:
| Parameter | Description |
|---|---|
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:
| Parameter | Description |
|---|---|
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.
| Parameter | Description |
|---|---|
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.
| Parameter | Description |
|---|---|
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.
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)'}")
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)
.
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 .
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 .
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 .
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)
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)
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 .
The PeriodicallyCallRemote makes remote invocations with a specified frequency.
PeriodicallyCallRemote(IDictionary options)
PeriodicallyCallRemote(String url,
IDictionary options)
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
).
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.
The
PaginationHelper
simplify the creation of paginated navigation on items.
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

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:

The
WizardHelper
is used in combination with MonoRail's Wizard support. It
exposes useful methods to create dynamic wizard navigation.
The
Effects2Helper
exposes the
script.aculo.us
script features.
Table of Contents
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.
A sample demonstrating the concepts in the section can be downloaded from !download/monorail/trunk/MonoRail.LocalizationSample.zip
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.
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
.
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.
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.
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.
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.
Table of Contents
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.
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
To set up a test project perform the following steps:
Create a Class Library project (usually you are going to use the same solution of the web project)
Add references to :
nunit.framework
Castle.MonoRail.Framework
Castle.MonoRail.TestSupport
Create test cases class extending BaseControllerTest
Call the PrepareController method passing Controller Instance, Area, Controller Name and Action Name.
Finally make Assertion calls on PropertyBag or other parts of the Controller that is under test. You can access the RailsEngineContext from BaseControllerTest.Context
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 .
Table of Contents
Integrations are components that provide integration to other services in the Castle stack.
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
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.
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.
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();
}
}
It is very important that you know what the AutoLoad property means and the behavior it governs.
| Enum field | Description |
|---|---|
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.
|
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:
An ActiveRecord SessionScope is created
in
OnBeginRequest()
The customer object will be looked up from database by its primary key.
The properties of the customer object are updated through databinding.
customer.Update()
is called and throws an exception.
The catch block executes.
The ActiveRecord SessionScope is
disposed in
OnEndRequest()
:
NHibernate checks whether there are pending changes.
The customer object is marked dirty, because its property values have been changed during databinding.
NHibernate flushes the session. During the flush, the changes of customer object are written back to the database.
An unhandled exception occurs.
The exception cannot be handled in your controller and an errorpage is shown.
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:
Make the session readonly and always flush it explicitly.
Remove the offending object from the session.
Create a
TransactionScope
and roll it back.
Use the
Validate
option of
ARDataBind
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();
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.
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
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
// ...
}
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
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.
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
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
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
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
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
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.
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.
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.
Make your web project reference the following assemblies:
Castle.DynamicProxy.dll
Castle.Model.dll
Castle.MicroKernel.dll
Castle.Windsor.dll
Castle.MonoRail.WindsorExtension.dll
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>
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; }
}
}
}
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
.
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.
Table of Contents
MonoRailConfigurationExtensionManagerIViewSourceLoaderIViewEngineIScaffoldingSupportIControllerFactoryIViewComponentFactoryIFilterFactoryIResourceFactoryIEmailSenderIEmailTemplateServiceIControllerDescriptorProviderIResourceDescriptorProviderIRescueDescriptorProviderILayoutDescriptorProviderIHelperDescriptorProviderIFilterDescriptorProviderIControllerTreeICacheProviderThere 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.
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.
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>
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>
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.
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."
Josh wrote a very interesting post about MonoRail and Url rewriting .
Colin Ramsay wrote about rewriting options for IIS .
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.
Dynamic actions and action providers are a way to create custom and dynamic functionality.
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();
}
}
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();
}
}
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 .
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.
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.
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.
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>
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.
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>
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.
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.
The configuration is read into the
MonoRailConfiguration
. It's also registered as a service
The services collected are instantiated and registered
The lifecycle is executed
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.
You have to use the
monorail
configuration node to override or add services to
MonoRail.
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.
Manages registered extensions dispatching events from Asp.Net infrastructure and from MonoRail services
Sits in front of the file system and from assembly resources. It is used by view engines to obtain view streams
Inspects Controller types building a descriptor of what has been defined using attributes
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
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.
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> ...