MonoRail First Example

本文介绍了使用 Castle MonoRail 框架进行 Web 开发的基本流程,包括项目创建、控制器和视图的使用、数据绑定及 ActiveRecord 的集成等关键概念。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Getting started with MonoRail

Castle MonoRail greatly simplifies web development. This Getting Started is going to show you how common tasks are handled and how MonoRail performs most of the repetitive work on your behalf.

Amongst other things you will learn

  • What is a controller and a view, and their relation

  • How to use layouts and rescues

  • How to use the SmartDispatcherController and the DataBinder

  • How to easily create a CRUD page

  • How Castle ActiveRecord integrates nicely with MonoRail

Although it should go without saying, there are no requirements to use ActiveRecord with MonoRail. You can use MonoRail with whatever data access approach you like. However, if you use ActiveRecord, there are some developed integrations that might save you a few keystrokes.

You can also download the complete example:

GettingStartedSampleVS2003.zip (15.26k)

GettingStartedSampleVS2005.zip (15.85k)

Proceed with Requirements.

Requirements

We assume that you have Castle Project assemblies on your local machine. If you don't, we encourage you to download the MSI installer distribution, as it makes the assemblies visible to Visual Studio.

The initial project skeleton will use the Castle MonoRail project wizard. But you can also create the same skeleton manually.

This Getting started will use a database at some point, and we assume that you are using MSSQL Server 2000, but any other database supported by NHibernate will do.

Proceed with Creating the project skeleton.

Creating the project skeleton

We encourage you to use the following project structure, as a convention:

Folders:

Solution:

The following sections show you how to create this structure using manually or by using the MonoRail wizard.

Using the MonoRail project wizard

If you have installed Castle Project using the MSI installer you are likely to have the Visual Studio Integration installed as well. If so, create a new MonoRail project using the Wizard.

Warning

Visual Studio 2005 users must download and install the Web Project support for Visual Studio, otherwise the wizard will not work.

  1. Open Visual Studio and go to New\Project...
    With Visual Studio.Net 2003:

    With Visual Studio.Net 2005:

  2. Enter GettingStartedSample as the project name

  3. The Wizard starts, select NVelocity View Engine and click Next

  1. Check the option to create a test project and click Finish. The Wizard will create the solution and the projects and configure everything for you. This might take a few seconds.

At this point you should have a project ready to run.

Proceed with Your first controller and view.

Creating the project manually on Visual Studio

If you haven't installed the VS integration or don't want to use the project wizard, you can create the project structure manually.

  1. Create an ASP.Net project on Visual Studio (or a Class Library if the ASP.Net Project is not available on your Visual Studio installation).

  2. Add references to the following assemblies:

  • Castle.MonoRail.Framework.dll: The MonoRail framework

  • Castle.MonoRail.Framework.Views.NVelocity.dll: The view engine we are going to use

  • Castle.Components.Binder.dll: The binder implementation

  • Castle.Components.Common.EmailSender.dll: The email service contracts

  • Castle.Components.Common.EmailSender.SmtpEmailSender.dll: The email service implementation

  • Castle.Core.dll: Core functionalities shared by Castle Projects

  • NVelocity.dll: The template engine

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.

  1. Finally configure the web.config file. You must register a handler, a http module and the MonoRail configuration section:

1.    Add the MonoRail configuration section:

2.                

 

3.                 <configuration>

4.                     <configSections>

5.                         <section

6.                             name="monorail"

7.                             type="Castle.MonoRail.Framework.Configuration.MonoRailSectionHandler, Castle.MonoRail.Framework" />

8.                     </configSections>

9.                

 

10.                 <monorail>

11.                     <controllers>

12.                         <assembly>GettingStartedSample</assembly>

13.                     </controllers>

14.                     

15.                     <viewEngine

16.                         viewPathRoot="Views"

17.                         customEngine="Castle.MonoRail.Framework.Views.NVelocity.NVelocityViewEngine, Castle.MonoRail.Framework.Views.NVelocity" />

18.                 </monorail>

19.                 

20.                 ...

21.        To use an extension like castle or rails, register the handler:

22.            

 

23.                 ...

24.                 

25.                 <system.web>

26.                     <httpHandlers>

27.                         <add

28.                             verb="*"

29.                             path="*.rails"

30.                             type="Castle.MonoRail.Framework.MonoRailHttpHandlerFactory, Castle.MonoRail.Framework" />

31.                     </httpHandlers>

32.                 </system.web>

33.                 

34.             </configuration>

35.        Finally register the http module:

36.            

 

37.                 ...

38.                 

39.                 <system.web>

40.                     <httpHandlers>

41.                         <add

42.                             verb="*"

43.                             path="*.rails"

44.                             type="Castle.MonoRail.Framework.MonoRailHttpHandlerFactory, Castle.MonoRail.Framework" />

45.                     </httpHandlers>

46.                     <httpModules>

47.                         <add

48.                             name="monorail"

49.                             type="Castle.MonoRail.Framework.EngineContextModule, Castle.MonoRail.Framework" />

50.                     </httpModules>

51.                 </system.web>

52.                 

53.             </configuration>

Proceed with Your first controller and view.

Your first controller and view

For any MonoRail application, the entry point is the Controller. That is the biggest difference from WebForms development where the entry point is the page itself. With MonoRail the controller is executed and decided whether it should render a view and which view. So the controller is the one with control over the application flow, not the pages/views. That reduces the views to their original role: present information, nothing more and nothing less.

It is time to create your first controller. If you have used the wizard to create the project, the controller is already there. Anyway, the controller class should be named HomeController and should exist under the Controllers folder:

Here is the Controller code:

 

namespace GettingStartedSample.Controllers
{
    using System;

 

    using Castle.MonoRail.Framework;

 

    [Layout("default"), Rescue("generalerror")]
    public class HomeController : SmartDispatcherController
    {
        public void Index()
        {
        }
    }
}

The controller name is, by default, grabbed from the type name. In this case the controller name from MonoRail's point-of-view is simply Home. This controller exposes just one action: Index. Actions are public non-static methods exposed by the controller class.

Although the action code block is empty, there is a behavior implied. When this action is executed, it will render a view named Index. Yes, the action name is used, as convention, to decide the view to render. The action can override this and select a different view using the RenderView method.

Before digging into this, let's create the view.

Creating the view and the layout view

You, as a careful reader that we know you are, have noticed the attributes used on the controller class:

 

[Layout("default"), Rescue("generalerror")]
public class HomeController : SmartDispatcherController

Those defines the following:

  • For each view rendered, use the layout named default which lies on the layouts folder
  • If any unhandled exception is threw by the action, render the rescue view generalerror which lies on the rescues folder

As stated before, we are using NVelocity view engine. NVelocity is a very simple template engine that supports condition statements, assignments, array creations, and iterate over collections. That is definitely all you need when all you want to do is rendering a view. You can learn more about NVelocity (and how we have improved it) on its page.

Creating the view for the Index action on the Home controller

The views are bound to a controller. In our case we are dealing with the Home controller, so on the Views folder, create a (or use the existing) home folder and create an index.vm file there:

Here is the home.vm contents:

 

<p>

 

Hello world from my first action.

 

</p>
    

Quick Note

You can make Visual Studio use the Html editor for .vm files. Use the Open With... option from the context menu.

Fairly simple view. Note that we did not used the html and body tags. We will left that to the layout.

Creating the layout

A layout defines the outter content of a rendered view. You can have multiples layouts on a project, and they should lie on the layouts folder.

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>    

The $childContent variable defines where the view content should be included on the layout. The $siteRoot variable will represent the root of the application. If the web application is using a virtual dir for example, it will be the virtual dir name. Otherwise it would evaluate to a simple /.

Remember that our controller used the [Layout("default")]? That will make every view rendered from our controller to use this layout.

Running the application

If you created the application with the wizard, just hit Debug\Start or F5. In Visual Studio 2003, Cassini will run. In VS 2005 the internal web server will run. Both allow debug.

On the browser, go to the application url and enter the controller name and the action name plus the extension you have selected. For example:

http://localhost:8081/home/index.rails

The browser should render the view content with the layout applied:

Running with IIS or on Mono

If you want to run the application using Microsoft Internet Information Services, you must associate the extension you selected (ie rails or castle) with the ASP.Net ISAPI.

For more information on this check the Installing document on the User's Guide.

More about actions and views

Views are pretty useless unless you pass parameters and data to it. Each view engine will treat parameters differently. With NVelocity the parameters will turn into variables.

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 DataToTheView action uses the PropertyBag to pass data to the view. It then invokes RenderView to customize the view to render. If we haven't invoked this one, the view name would be datatotheview.

Now lets create the data.vm which should lie on the views\home folder:

 

<h3> Hello $name! </h3>

 

<h4> Today is $today.ToShortDateString() </h4>

Run the application and go to the home/datatotheview.rails to test your work.

Creating the rescue view

Our controller also uses a rescue. Lets create the rescue view to report errors nicely.

Create a generalerror.vm on the views\rescues folder:

 

#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>

Now lets force an exception on a new action just to see the rescue working.

 

[Layout("default"), Rescue("generalerror")]
public class HomeController : SmartDispatcherController
{
    public void Index()
    {
    }
    
    public void ErroneousAction()
    {
        throw new Exception("Forced exception to test Rescue");
    }
    
    public void DataToTheView()
    {
        PropertyBag["name"] = "John Doe";
        PropertyBag["today"] = DateTime.Now;
        
        RenderView("data");
    }
}

Run the application and go to the home/erroneousaction.rails.

Take a breath

That was a crash course on controllers and views. More in-depth information can be found on the documentation.

Proceed with Using the SmartDispatcherController.

Using the SmartDispatcherController

Although our controller extends from SmartDispatcherController we haven't used any of its feature. The SmartDispatcherController, as the name implies, implements a smart way to invoke actions. Query string entries and form entries can be bound actions parameters. For example, suppose you have the following form on a view:

 

<form action="/home/saveinformation.rails" 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>

You can create the action SaveInformation in a way that it will expect the 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");
    }
}

The code above saves you the hassle of using the Params or Form name-value collections. It is basically equivalent to the following code:

 

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

But much cleaner and simpler. It is able to handle a diversity of types, for more on this check the documentation.

Using the DataBind attribute

You can also use the SmartDispatcherController to populate entire objects. To exemplify that lets create a contact form so the user can contact some department from our ficticious company.

1.    Create a new controller named ContactController and include two actions: ContactForm and SendContactMessage.

2.           

 

3.           namespace GettingStartedSample.Controllers
4.           {
5.               using System;
6.               using Castle.MonoRail.Framework;
7.           

 

8.               [Layout("default"), Rescue("generalerror")]
9.               public class ContactController : SmartDispatcherController
10.           {
11.               public void ContactForm()
12.               {
13.               }
14.               
15.               public void SendContactMessage()
16.               {
17.               }
18.           }
}

19.Create a class Contact that represents the contact information.

20.       

 

21.       public class Contact
22.       {
23.           private string from;
24.           private string area;
25.           private string subject;
26.           private string message;
27.       

 

28.           public string From
29.           {
30.               get { return from; }
31.               set { from = value; }
32.           }
33.       

 

34.           public string Area
35.           {
36.               get { return area; }
37.               set { area = value; }
38.           }
39.       

 

40.           public string Subject
41.           {
42.               get { return subject; }
43.               set { subject = value; }
44.           }
45.       

 

46.           public string Message
47.           {
48.               get { return message; }
49.               set { message = value; }
50.           }
}

51.Now create the view for the ContactForm action. The view name should be contactform.vm and should be put on Views\Contact folder.

52.       

 

53.       <h2>Contact us!</h2>
54.       

 

55.       <p>
56.       We are interested in hearing from you.
57.       </p>
58.       

 

59.       <form action="SendContactMessage.rails" method="post">
60.       

 

61.       <p>
62.       From: $FormHelper.TextField("contact.from")
63.       </p>
64.       

 

65.       <p>
66.       Area: $FormHelper.TextField("contact.area") (Sales, Support)
67.       </p>
68.       

 

69.       <p>
70.       Subject: $FormHelper.TextField("contact.subject", "%{size='30'}")
71.       </p>
72.       

 

73.       <p>
74.       Message: <br/> 
75.       $FormHelper.TextArea("contact.message", "%{cols='35', rows='6'}")
76.       </p>
77.       

 

78.       <hr />
79.       

 

80.       <p>
81.       <input type="submit" value="Send the message" />
82.       </p>
83.       

 

</form>

Note that we used the FormHelper which handles generation of form elements. You can (and should) learn more information about helpers and especially the FormHelper on the documentation.

You should also note that the each generated field on the html will be prefixed with contact, for example contact.name. This is important as to use MonoRail's data bind you must prefix the form elements to avoid name clashes.

84.Finally change the action to bind the form data to the Contact class:

85.       

 

86.       public void SendContactMessage([DataBind("contact")] Contact contact)
87.       {
88.           // Pretend to save the contact ...
89.           
90.           // ..work work work..
91.           
92.           // Now lets add the contact to the property bag
93.           // so we can render a nice message back to the user
94.           
95.           PropertyBag["contact"] = contact;
96.           
97.           RenderView("confirmation");
}

We used the DataBindAttribute on the action parameter. Its first parameter is the prefix used on the form. In our case it is contact. When this action is invoked, MonoRail will try to match the form data with the properties on the Contact class and create an new instance populating the properties.

98.It would be nice to create the confirmation view too:

99.       

 

100.   <p>
101.   

 

102.   Thanks $!contact.from, we have received your message and will
103.   get back to your shortly.
104.   

 

105.   </p>
    

Now test your work by running the application and accessing /contact/contactform.rails. Fill the form elements and submit it.

Proceed with Bringing ActiveRecord to the party.

Bringing ActiveRecord to the party

In order to explorer some more realistics scenarios you will face in daily developments, why not use ActiveRecord as our database access approach?

All you have to do is reference the assemblies, set up a database and configure and initialize ActiveRecord:

  1. First of all, add references to the following assemblies:

  • Castle.ActiveRecord.dll

  • Castle.DynamicProxy.dll

  • Iesi.Collections.dll

  • log4net.dll

  • NHibernate.dll

Now include a section on the web.config so you can keep ActiveRecord's configuration there:

3.          

 

4.           <configuration>

5.               <configSections>

6.                   <section

7.                       name="monorail"

8.                       type="Castle.MonoRail.Framework.Configuration.MonoRailSectionHandler, Castle.MonoRail.Framework" />

9.                   <section

10.                   name="activerecord"

11.                   type="Castle.ActiveRecord.Framework.Config.ActiveRecordSectionHandler, Castle.ActiveRecord" />

12.           </configSections>

13.           

14.           <activerecord isWeb="true">

15.               

16.               <config>

17.                   <add

18.                       key="hibernate.connection.driver_class"

19.                       value="NHibernate.Driver.SqlClientDriver" />

20.                   <add

21.                       key="hibernate.dialect"                

22.                       value="NHibernate.Dialect.MsSql2000Dialect" />

23.                   <add

24.                       key="hibernate.connection.provider"    

25.                       value="NHibernate.Connection.DriverConnectionProvider" />

26.                   <add

27.                       key="hibernate.connection.connection_string"

28.                       value="Data Source=.;Initial Catalog=test;Integrated Security=SSPI" />

29.               </config>

30.               

31.           </activerecord>

Quick Note

The configuration above uses SQL Server 2000. If you are using a different database you need to change the dialect. Please refer to the Xml Configuration Reference document for more information.

This configuration access a database catalog named test. Make sure it exists or change the configuration to access an existing database catalog.

  1. Create two models to represent products and suppliers. You can create the classes on the Models folders

The Supplier.cs:

 

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

        }

    }

}

The Product.cs:

 

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

        }

    }

}

  1. The last step is to initialize ActiveRecord passing the configuration. The best place to do that in a web application is on the global.asax related class. So create a global.asax if none exists and on the related class, use the Application_OnStart event to initialize ActiveRecord:

The global.asax file:

 

<%@ Application Inherits="GettingStartedSample.GlobalApplication" %>

The GlobalApplication.cs file:

 

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 next time you run the application it will create the tables for you, so you can comment the line that invokes CreateSchema after that.

In the following pages we will have some fun using scaffolding and creating CRUD pages.

Proceed with Using ActiveRecord Scaffolding.

Using ActiveRecord Scaffolding

First lets handle inclusion the easy way. By using ActiveRecord Scaffolding support we can have a crud page with no effort. However Scaffolding is meant to be used to prototype applications only, as you can get something working really fast. During the application lifecycle you should replace the scaffolded controller with a more suitable crud implementation (which we will see in the next page).

Enabling ActiveRecord Scaffolding is real simple.

  1. First of all, add references to the following assemblies:

  • Castle.MonoRail.ActiveRecordScaffold

  • Castle.MonoRail.ActiveRecordSupport

Now create a controller to manage the Supplier class:

3.          

 

4.           namespace GettingStartedSample.Controllers

5.           {

6.               using System;

7.               using Castle.MonoRail.Framework;

8.               using GettingStartedSample.Models;

9.          

 

10.           [Scaffolding(typeof(Supplier))]

11.           public class SupplierController : Controller

12.           {

13.           }

}

Note the usage of the ScaffoldingAttribute

That is it. Now run the application and direct your browser to /supplier/list.rails



Proceed with Creating a crud page with DataBinder.

Creating a CRUD page with DataBinder

CRUD stands for Create, Retrieve, Update and Delete. Most applications have some kind of CRUD funcionality, and we can assume that every programmer had to deal with CRUD at some point.

As you remember, we have created a Supplier and Product classes. We used scaffolding to create the crud for the Supplier class. Now let's code the CRUD for the Product class manually. You'll see that it is also something really simple.

The first thing you must do is create the ProductController:

 

namespace GettingStartedSample.Controllers

{

    using System;

    using Castle.MonoRail.Framework;

    using GettingStartedSample.Models;

 

    [Layout("default"), Rescue("generalerror")]

    public class ProductController : SmartDispatcherController

    {

    }

}

Now we are ready.

Listing

Create a List action on the controller:

 

public void List()

{

    PropertyBag["products"] = Product.FindAll();

}

This code makes all products available to the view. Now create the list.vm:

 

<h3>Product list</h3>

 

<p>

<a href="new.rails">Create new Product</a>

</p>

 

<table width="100%" border="1" cellpadding="2" cellspacing="0">

<tr>

    <th>Id</th>

    <th>Name</th>

    <th>Supplier</th>

    <th>&nbsp;</th>

</tr>

#foreach($product in $products)

<tr>

    <td align="center">$product.Id</td>

    <td align="center">$product.Name</td>

    <td align="center">$product.Supplier.Name</td>

    <td align="center">

        <a href="edit.rails?id=${product.Id}">Edit</a> |

        <a href="delete.rails?id=${product.Id}">Delete</a>

    </td>

</tr>

#end

</table>

Test your working accessing /product/list.rails

Creating

To insert a new product we will use two actions:

  • New: which will present the form

  • Create: which uses the data supplied on the form to create the Product on the database

 

public void New()

{

    PropertyBag["suppliers"] = Supplier.FindAll();

}

 

public void Create([DataBind("product")] Product prod)

{

    try

    {

        prod.Create();

   

        RedirectToAction("list");

    }

    catch(Exception ex)

    {

        Flash["error"] = ex.Message;

        Flash["product"] = prod;

       

        RedirectToAction("new");

    }

}

The New actions makes all suppliers available to the view. This is required to populate a select with all suppliers available.

The new.vm view follows:

 

<h3>New Product</h3>

 

#if($Flash.error)

<p style="color: red; font-weight: bold;">

    $Flash.error

</p>

#end

 

<form action="create.rails" 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>

Test your working accessing /product/new.rails

Updating

Updating is not very different from creating. First create two actions: 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);

    }

}

We can also use a different Edit overload that will be used when the Update catch block redirects it back to the Edit:

 

public void Edit(int id, [FlashBinder] Product product)

{

    PropertyBag["suppliers"] = Supplier.FindAll();

}

The edit.vm view is pratically the same as the new.vm. The FormHelper is clever enough to populate the fields and select the correct item on the select element.

 

<h3>Edit Product</h3>

 

#if($Flash.error)

<p style="color: red; font-weight: bold;">

    $Flash.error

</p>

#end

 

<form action="update.rails" 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>

Deleting

Deleting is the easiest one. Add the Delete action:

 

public void Delete(int id)

{

    Product product = Product.FindById(id);

   

    product.Delete();

   

    RedirectToAction("list");

}

So, do you miss WebForms at all?

Proceed with Getting more.

Getting More

This Getting Started gave you a very fast introduction to what MonoRail is capable of and how to use it. But there are much more to learn. Explore the samples, stretch and bend it as you wish, and consult the documentation which contains much more in-depth information.

Installing MonoRail

MonoRail does not require any complex installation. However, you must associate the extension you want to use with the ASP.Net ISAPI on IIS.

Installing on Internet Information Services

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

Second step: open the web site configuration

  1. Right-click the Default Web Site item and choose Properties
  2. Select the Home Directory tab
  3. Click Configurations

Third step: associate the file extension

  1. Click Add
  2. Select the ISAPI DLL. You can copy-and-paste the complete DLL file name from another extension, such as .aspx. In most systems it will be something like C:\WINDOWS\Microsoft.NET\Framework\v1.1.4322\aspnet_isapi.dll (for .NET 1.1) or C:\WINDOWS\Microsoft.NET\Framework\v2.0.50727\aspnet_isapi.dll (for .NET 2.0).
  3. Fill in the extension (for example .rails) as the file extension (make sure you do not omit the leading dot)
  4. Uncheck the Check file exists check box

Quick Note

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

Running with Cassini

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.

Mono and XSP

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

xsp --port 80

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

Running with Apache/Mono

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

Configuring Apache

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.

Apache Httpd 2

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

Deploying the application

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

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

Deploying to a shared host

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

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

 

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

 

转载于:https://www.cnblogs.com/wgx1323/archive/2006/11/07/552500.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值