![]() |
Web Development »
ASP.NET »
Howto
Intermediate
License: The Code Project Open License (CPOL)
Monorail Hands-OnBy Marcelo Ricardo de OliveiraIn this article I will present a sample application using Monorail framework and providing the basic concepts of the design pattern known as MVC. |
C# (C# 2.0), Windows (WinXP, Win2003, Vista), .NET (.NET 2.0), ASP.NET, Dev
|
||||||||||
|
Advanced Search |
|
|
|
||||||||||||||||
IntroductionI'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 WebForm applications, not only because of Monorail, but also due to the arising of the new ASP.NET MVC framework. The MVC PatternMVC 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 is the user interface components. They know how to present data, and expose graphic elements such as buttons, list 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. The Monorail FrameworkMonorail, developed by Castle Project since 2003, is an open source MVC framework for .Net web applications, inspired on 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 Asp.Net infrastructure (e.g.: session management, events and security). Having said that, we can enumerate some advantages of Monorail over traditional WebForm applications:
Monorail Hands-On: The Northwind Traders Sample ApplicationThe goal of 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 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. Url routingUnlike 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]
The Domain ModelI could have added more entities to our model, but for the sake of simplicity, I decided to keep only Product and Supplier. Each entity class is inherited from 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:
The ViewsThe 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 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 ControllersThe 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 Suppose you request the following url to the website:
http://localhost/monorail/product/index.rails
The following events will take place:
Downloading and Installing the Sample ApplicationInstalling Northwind DatabaseSince the sample application depends on the Northwind database: IIS ConfigurationA request to a Monorail application is identified by the 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 you 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> Httphandler tag in Web.config file But in order to the .rails extension 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
Downloading the Sample ApplicationNext, 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! Downloading the Source CodeIn order to open the Northwind Monorail project attached to this article, you should first install Castle Project Release Candidate 3 MSI. History2008-02-19: created. |
| 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: |
Copyright 2008 by Marcelo Ricardo de Oliveira Everything else Copyright © CodeProject, 1999-2009 Web19 | Advertise on the Code Project |