![]() |
Web Development »
ASP.NET »
Samples
Intermediate
License: The Code Project Open License (CPOL)
Monorail Hands-OnBy Marcelo Ricardo de OliveiraIn this article, I will present a sample application using the Monorail framework and provide the basic concepts of the design pattern known as MVC. |
XML, C# 2.0, .NET (.NET 2.0), WinXP, Win2003, Vista, ASP.NET, Dev
|
|
Advanced Search Add to IE Search |
|
|
|
||||||||||||||||
I've decided to write this article for two reasons: first, my enthusiasm with the Monorail framework, and second, the .NET community has displayed a growing interest on MVC as an alternative to traditional WebForms applications, not only because of Monorail, but also due to the rise of the new ASP.NET MVC framework.
MVC is the acronym for Model-View-Controller, a design pattern that enforces the separation of concerns on the presentation layer. Note that MVC is in no way limited to web applications; it might also be applied to other platforms that expose a presentation layer, e.g., a desktop environment.
MVC enables the decoupling of data access logic (model) from user interface logic (view), and establishing the controller as an intermediate component.
Model: Represents the domain, that is, the entities that encapsulate raw data plus the domain logic. Note that the MVC pattern doesn't mention how the data persistence should be implemented. Usually, it is encapsulated by the Model component.
View: This comprises the user interface components. They know how to present data, and expose graphic elements such as buttons, lists, and text boxes, that allow user interaction.
Controller: The controller is the component that orders the rendering of the view, responds to events of the user interface, and invokes changes in the model.

Although some authors conclude that WebForms implement the MVC pattern, it must be said that in WebForms, the controller rule is shared between the ASPX file, the code-behind, and the web controls, which goes against the separation of concerns and the single responsibility principle.
Monorail, developed by Castle Project since 2003, is an Open Source MVC framework for .NET web applications, inspired on the Ruby on Rails Action Pack. The Monorail flow could be described in a few lines, but since a picture is worth 10,000 words...

Note that Monorail has an implementation of IHttpHandler, so it still takes advantage of the ASP.NET infrastructure (e.g.: session management, events, and security).
Having said that, we can enumerate some advantages of Monorail over traditional WebForm applications:
The goal of the Northwind Traders application is to provide simple CRUD functionalities for Products and Suppliers of the company. That is, we must be able to create, retrieve, update, and delete products and suppliers of Northwind Traders through our Monorail application.
The structure of the Northwind Traders project follows Monorail standards: we have a Monorail project, much like a traditional WebForms project, and it has separate folders for Models, Views, and Controllers.
The Models folder contains the entities Product and Supplier, which are persisted to the SQL Server database through the ActiveRecord framework.
The Views folder contains the .vm files, which are basically NVelocity template files used by Monorail to generate the final HTML code for the view.
The Controllers folder contains the controller classes, ProductController and SupplierController. Each controller has the methods representing the actions emitted against the controller to perform the CRUD functionalities on the entities.
Unlike WebForms websites, where each URL is redirected to a physical file in the website, in Monorail, the URL is written to be re-routed not to a file, but to a specific action in a specific controller, following this format:
http://website/MonorailApplication/controller/action.rails
Example:
http://localhost/monorail/product/masterdetail.rails
The result of this request is the rendering of the following page:
Let's try to understand how we got there from our URL:
http://localhost/monorail[1]/product[2]/masterdetail[3].rails[4]
ProductController class.MasterDetail method in the ProductController class.httphandlers tag) that tells ASP.NET to redirect every ".rails" request to the Monorail Framework.I could have added more entities to our model, but for the sake of simplicity, I decided to keep only the Product and Supplier. Each entity class is inherited from the ActiveRecordBase class, which automatically provides Refresh, Delete, Update, Create, and Save functionalities.
The code below represents the Product class:
using System;
using Castle.ActiveRecord;
namespace Northwind_Monorail.Models
{
[ActiveRecord("Products")]
public class Product : ActiveRecordBase<Product>
{
private int id;
private String name;
private decimal price;
private Supplier supplier;
[PrimaryKey("ProductID")]
public int Id
{
get { return id; }
set { id = value; }
}
[Property("ProductName")]
public string Name
{
get { return name; }
set { name = value; }
}
[Property("UnitPrice")]
public decimal Price
{
get { return price; }
set { price = value; }
}
[BelongsTo("SupplierId")]
public Supplier Supplier
{
get { return supplier; }
set { supplier = value; }
}
public new static Product[] FindAll()
{
Product[] products = (Product[])FindAll(typeof(Product));
return products;
}
public static Product FindById(int id)
{
return (Product) FindByPrimaryKey(typeof(Product), id);
}
}
}
Just a few notes on the Product class:
Product class is decorated by the [ActiveRecord("Products")] attribute, which means that the Product class is mapped to the Products table in the Northwind database.Id property is decorated by [PrimaryKey("ProductID")], which means that it is mapped to Products' primary key column (ProductID) in the Northwind database.[Property()] attribute.Product class has a reference to the Supplier class. This is done by the Supplier property, which is of Supplier type. The attribute [BelongsTo("SupplierId")] informs which property of the Supplier (SupplierID) is the foreign key property.The view files in our project have the .vm extension, which means they will be processed by NVelocity. NVelocity is a template engine; that is, NVelocity files will be injected with information from controllers in order to form the final HTML code. You can use other view engines, like Brails or even .aspx (although this last one is not advisable, since you wouldn't be able to use Web Controls). But here, we will deal with the NVelocity view engine only.
As an example, the following code snippet represents the index.vm file:
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"
"http://www.w3.org/TR/html4/strict.dtd">
<html lang="en">
<head>
$AjaxHelper.GetJavascriptFunctions()
$ScriptaculousHelper.GetJavascriptFunctions()
<script language="javascript" type="text/javascript">
.
.
.
<div id="headerwrap">
<div id="header">
#parse("menu\\headermenu.vm")
</div>
<div id="middlewrap">
<div id="middle">
<div id="sidebar">
#parse("menu\\shortcut.vm")
</div>
<div id="content">
<div id="statusbar"></div>
<div id="details">#parse("product\\edit.vm")<div>
<div id="search">#parse("product\\search.vm")</div>
<div id="list">#parse("product\\list.vm")</div>
</div>
</div>
</div>
</div>
<div id="footerwrap">
<div id="footer">
</div>
</div>
</body>
<html>
Notice the #parse directives in the view above. The role of the #parse directive is to render the content of another view within the view, so that you can keep clean, small, and cohesive views for your website.
The following picture explains how partial views are rendered by the main product view (index view):
The code below represents the ProductController class:
using System;
using System.Collections;
using System.Collections.Generic;
using Castle.MonoRail.Framework;
using Castle.MonoRail.Framework.Helpers;
using Castle.Monorail.JSONSupport;
using Newtonsoft.Json;
using NHibernate.Expression;
using Northwind_Monorail.Models;
using Northwind_Monorail.Queries;
namespace Northwind_Monorail.Controllers
{
[Layout("default"), Rescue("generalerror"),
Helper(typeof(JSONHelper), "Json")]
public class ProductController : SmartDispatcherController
{
.
.
public void Index([DataBind("product",
Validate = true)] Product product)
{
PropertyBag["products"] = new Product();
string productName = "";
int supplierId = 0;
PropertyBag["products"] =
PaginationHelper.CreatePagination(this,
GetProducts(productName, supplierId), 10);
PropertyBag["suppliers"] = GetSuppliersWithABlankItem();
List<Shortcut> shortcuts = new List<Shortcut>();
Shortcut scProduct = new Shortcut();
scProduct.Text = "Products";
scProduct.Image = "\\Images\\products.gif";
scProduct.Url = "\\product\\Index.rails";
scProduct.DetailUrl = "\\product\\edit.rails";
scProduct.SearchUrl = "\\product\\search.rails";
scProduct.ListUrl = "\\product\\list.rails";
scProduct.Tooltip = "Manage Products";
Shortcut scSupplier = new Shortcut();
scSupplier.Text = "Suppliers";
scSupplier.Image = "\\Images\\suppliers.gif";
scSupplier.Url = "\\supplier\\Index.rails";
scSupplier.DetailUrl = "\\supplier\\edit.rails";
scSupplier.SearchUrl = "\\supplier\\search.rails";
scSupplier.ListUrl = "\\supplier\\list.rails";
scSupplier.Tooltip = "Suppliers";
shortcuts.Add(scProduct);
shortcuts.Add(scSupplier);
PropertyBag["shortcuts"] = shortcuts;
}
.
.
.
Suppose you request the following URL to the website:
http://localhost/monorail/product/index.rails
The following events will take place:
index() method) of an instance of the ProductController class.PropertyBag list.Since the sample application depends on the Northwind database:
A request to a Monorail application is identified by IIS by the URL extension. Usually, that extension is .rails (like in our sample), but you can use other extensions, if they aren't in use yet. When users call a URL of your Monorail web application, the Internet Information Server must first pass that information to the ASP.NET ISAPI, so that the ASP.NET framework can read your app's web.config, where there is an explicit instruction to hand over control to the Monorail framework whenever the .rails extension is found:
<httpHandlers>
<add verb="*" path="*.rails"
type="Castle.MonoRail.Framework.MonoRailHttpHandlerFactory,
Castle.MonoRail.Framework"/>
.
.
.
</httpHandlers>
But, in order for the .rails extension to be processed, it must first be associated with the ASP.NET ISAPI. You can do it by configuring IIS as follows:



For more detailed information, see: Installing Monorail.
Note: We had to perform this configuration above in the IIS because in this sample, we are using the .rails extension. However, as our reader mentifex pointed out in his comments, you can make your life easier by changing your web.config file to use the .ashx extension instead of .rails. ASHX (ASP.NET Web Handler File) is an ASP.NET native extension, and you don't have to configure manually its mapping on the IIS, since it is already mapped to the ASP.NET ISAPI. Besides, in cases when you don't have permission to change your IIS configuration, .ashx is your only choice. Just change your web.config to map .ashx files to the Monorail framework, and you're done.
Next, you download the Northwind Traders application attached to this article to a local folder:
Then, you create a virtual directory for the application:
Important: Make sure your site was created for ASP.NET 2.0.
That's it!
In order to open the Northwind Monorail project attached to this article, you should first install Castle Project Release Candidate 3 MSI.
| You must Sign In to use this message board. | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||
General
News
Question
Answer
Joke
Rant
Admin
|
PermaLink |
Privacy |
Terms of Use
Last Updated: 19 Feb 2008 Editor: Smitha Vijayan |
Copyright 2008 by Marcelo Ricardo de Oliveira Everything else Copyright © CodeProject, 1999-2009 Web18 | Advertise on the Code Project |