Kurser och utbildning av IT-proffs och systemutvecklare

i Sök

Cornerstones utvecklarblogg

Alla Taggar

All Tags » MVC Framework   (RSS)

  • ASP.Net MVC Framework 2 - A step by step guide to create a simple Web Application

    This post is different from my others, this post is a step by step guide to create a ASP.NET MVC Web application. You will learn to use most of the new features in the Preview 2 version of the ASP.NET Framework, you will also get a basic understanding about how to use LINQ to SQL. This step by step guide shouldn't be used as a "best way to create and design" a web applications by using the ASP.NET MVC Framework, this guide should only be used to get started with the MVC Framework in a fast and easy way and learn different ways to use some of the features shipped with the ASP.Net MVC Framework Preview 2.

    In this step by step guide you are going to create a web based application where you will be able to list and modify customers. This step by step guide will use C# 3.0 and the Northwind database.

     

    1. Open Visual Studio 2008

     

    2. Create a new Project and select the ”ASP.NET MVC Web Project” project and name it to “CustomerApp”. When you press the OK button a dialogue will be displayed to create a Test project for the solution.

    Select “No. do not generate a unit test project”, and press the OK button.

    Note: In this step by step guide you will not use unit test.

     

    3. Open global.asax and walk through the code. In Global.asax you can specify your routes. In this step by step guide you will use the default settings.

    The first thing to do is to create your business objects (your Model) which your Controllers will interact with. In this step by step guide you will use LINQ to SQL, even if it’s not advisable to generate domain models out from a data source you will learn how to do it, only to get up and running fast.

    Note: The business objects belongs to the Model in the MVC pattern.

     

    4. Right click on the Models folder in the Solution Explorer and select Add > New Item.

    Select the “LINQ to SQL Classes” template and name the file to “Northwind.dbml” and press the Add button.

    To create your Model out from a data source you need to add a database connection to Visual Studio. This can be done by using the Server Explorer. In the Solution Explorer, right click on the Data Connections node and select Add Connection. Enter the following information to connect to the Northwind database:

    Server Name: (local)

    Select the Northwind database, test the connection and press OK if you were able to connect to the database.

    A connection to the Northwind database will now be added under the “Data Connections” node in the Server Explorer. Expand the database and the Tables nodes; drag out the Customer table into the design view of the Northwind.dbml file. Save the file. When you save the file, LINQ to SQL will generate a Customer object for you where the columns from the Customers table will be added as properties to the Customer object. LINQ to SQL will also generate a DataContext object (NorthwindDataContext) which you will use later to access your information from the data source. Expand the Northwind.dbml file in the Solution Explorer, double click on the Northwind.designer.cs file and walk through the code. LINQ to SQL uses attributes to map classes to a table.

    Now it’s time to create the Controller which you are going to use in this application.

     

    5. Right click on the Controllers folder in the Solution Explorer and add a new item and select the “MVC Controller Class” template, name it to “CustomerController”.

     

    6. Add a private field of the type NorhwindDataContext to the Controller and name the field to “northwindDataContext”. Set the field to an instance of the NorthwindDataContext object.

    Note: The NorthwindDataContext class will exist in the CustomerApp.Models namespace.


    using
    CustomerApp.Models; namespace CustomerApp.Controllers { public class CustomerController : Controller, IDisposable { private NorthwindDataContext northwindDataContext = new NorthwindDataContext(); //... } }


    7. Make sure the CustomerController implements the IDisposable interface, and implement the Dispose method and make sure it will make a call to the northwindDataContext’s Dispose method. This will make sure the NorthwindDataContext’s Dispose method will be called when the Controller have been used.


    public class
    CustomerController : Controller, IDisposable { private NorthwindDataContext northwindDataContext = new NorthwindDataContext(); //... public void Dispose() { this.northwindDataContext.Dispose(); } }

     

    8. Add an Action method and name it to “List”. The List action will retrieve all Customers from the Customers table of the Northwind database. LINQ to SQL will be used to get all the Customers, order the Customers by CompanyName. When all customers are retrieved make sure to render a View with the name “Customers” (The View will be added later to the project), the View should render a generic list of Customer objects.


    public void
    List() { List<Customer> customers = (from customer in northwindDataContext.Customers orderby customer.CompanyName select customer).ToList(); RenderView("Customers", customers); }


    9. Create a View with the name “Customers” which will render the generic list of Customer objects. To add a View, right click on the Views folder and add a new folder with the name “Customer”. Right click on the Customer folder and add a new item, select the “MVC View Page” template and name the View to “Customers”. Make sure the View data inherits from ViewPage<List<CustomerApp.Models.Customer>>. This will make sure the Model which is passed as an argument to the RenderView method is a known and typed for the View. Open the file Customers.aspx.cs file, replace so the page inherits from ViewPage<List<CustomerApp.Models.Customer>> instead of ViewPage.

     

    public partial class Customers : ViewPage<List<CustomerApp.Models.Customer>>
    {
    }

     

    10. Add code to render a list of Customers to the View, make sure the CustomerID and ContactName are listed. Open the Customer.aspx file, add an inline-code block and use foreach to iterate through the Customers, use the ViewData property to get access to the generic list of Customer objects.


    <%
    @ Page Language="C#" AutoEventWireup="true" CodeBehind="Customers.aspx.cs" Inherits="CustomerApp.Views.Customer.Customers" %> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" > <head runat="server"> <title></title> </head> <body> <div> <table> <% foreach ( var customer in ViewData) { %> <tr> <td><%=customer.CustomerID %></td> <td><%=customer.ContactName %></td> </tr> <% } %> </table> </div> </body> </html>

     

    Make sure you have saved all files, compile and run the application. To the end of the URL add “Customer/List” and press enter. The URL will look something like this: http://localhost:<port number>/Customer/List.

    Your application will now make sure the Controller “CustomerController” will be used and also the Action method “List” will be invoked. If everything works, you will now see a list of CustomerID and ContactName.

    Note: The route that was defined in global.asax looks like this:


    {controller}/{action}/{id}


    When you enter a URL like “Customer/List”, the Customer will be the name of the Controller to be used, and the List the name of the Action method to be executed, the id in the route will be empty and will be ignored.

     

    11. Add a new Action method to the CustomerController and name it to “View”. The View method should have one argument with the name ID and the type of String. Make sure the method will use LINQ to SQL to get a Customer from the Customers table which matches the CustomerID passed as an argument. If a customer is not found, make sure an ApplicationExcpetion is thrown, for example with the following message “Can’t find a Customer with the CustomerID <customerID>”. Make sure to render a View with the name “Customer”. The View should render a detail view of a Customer.


    public void
    View(string ID) { Customer customer = (from c in northwindDataContext.Customers where c.CustomerID == ID select c).FirstOrDefault(); if (customer == null) throw new ApplicationException( String.Format("Can't find a Customer with the CustomerID {0}", ID)); RenderView("Customer", customer); }


    12. Add a new View to the Views/Customer folder and name it “Customer”. Make sure the Customer View will inherit form the ViewPage<CustomerApp.Models.Customer> class.


    public partial class
    Customer : ViewPage<CustomerApp.Models.Customer> { }


    13. Open the Customer.aspx page and add inline-code block to show detail information about a Customer, make sure input text elements is used to display the content of the Customer (you will later in the lab update the Customer). Set the name attribute of the input text element to the same name as the property of the Customer object it should display. The CustomerID should be displayed as a simple text, and also be a value of an input hidden field with the name “CustomerID”. The reason why you will use a hidden field with the CustomerID, is because you will later use the MVC Frameworks “databinding” feature.

    Note: The Customer object has several properties, and to make this lab simple you can only use some of them, CustomerID, CompanyName, ContactName and Phone.

    Leave the <Form> elements action attribute empty at the moment.


    <%
    @ Page Language="C#" AutoEventWireup="true" CodeBehind="Customer.aspx.cs" Inherits="CustomerApp.Views.Customer.Customer" %> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" > <head runat="server"> <title></title> </head> <body> <form method="post" action=""> <table> <tr> <td>Customer ID:</td> <td><%= ViewData.CustomerID %></td> </tr> <tr> <td>Company name:</td> <td><input type="text" name="CompanyName" value="<%= ViewData.CompanyName %>" /></td> </tr> <tr> <td>Contact Name:</td> <td><input type="text" name="ContactName" value="<%= ViewData.ContactName %>" /></td> </tr> <tr> <td>Phone:</td> <td><input type="text" name="Phone" value="<%= ViewData.Phone %>" /></td> </tr> </table> <input type="hidden" name="CustomerID" value="<%= ViewData.CustomerID %>" /> </form> </body> </html>


    Note: Instead of writing the whole <input> element, you can also use the Html help method:


    <%
    = Html.TextBox("CompanyName", ViewData.CompanyName) %>


    This method will render the following output:


    <input type=”text” size=”20” name=”CompanyName” id=”CompanyName” value=”My Company”/>


    Writing HTML will give you more control over the HTML which will be sent to the client.

    Save your View and build your solution. Start the application and to the end of the URL enter “Customer/View/ALFKI”. The URL will look something like this: http://localhost:<port number>/Customer/View/ALFKI. The Customer controller will be used and its Action method View will be called based on the route in global.asax the value after the specified Action method will be passed as an argument to the specified Action method. The argument is mapped to the argument with the name “ID”.

    If everything works, you will now see a detail view over the Customer with the CustomerID “ALFKI”.

     

    14. Open the Customers.aspx View and make sure the listed CustomerID instead will be a HyperLink, which will have the following URL pattern: “/Customer/View/<CustomerID>”.

     

    <%@ Page Language="C#" AutoEventWireup="true" CodeBehind="Customers.aspx.cs" Inherits="CustomerApp.Views.Customer.Customers" %>
    
    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
    
    <html xmlns="http://www.w3.org/1999/xhtml" >
    <head runat="server">
        <title></title>
    </head>
    <body>
        <div>
            <table>
            <% foreach ( var customer in ViewData) { %>
                <tr>
                    <td>
                    <a href="/Customer/View/<%=customer.CustomerID %>"><%=customer.CustomerID %></a>
                    </td>
                    <td><%=customer.ContactName %></td>
                </tr>
            <% } %>
            </table>
        </div>
    </body>
    </html>
    


    Note: Instead of writing the <a> element you can use the Html helper method to let it create an ActionLink, for example like this:


    <%=Html.ActionLink<CustomerApp.Controllers.CustomerController>(a => a.View(customer.CustomerID), customer.CustomerID) %>


    Make sure you have saved all files, compile and run the application. To the end of the URL add “Customer/List” and press enter. The URL will look something like this: http://localhost:<port number>/Customer/List.

    If everything works correct, you will now see a list of Customers and the CustomerID will not be a HyperLink. Press on a CustomerID and make sure it will navigate to the Customer View and display detail information about the selected Customer.

    Note: The URL of the HyperLink’s href attribute does not point to a specific file, instead if will point to the Controller and the Action method to be executed when it’s pressed.

     

    15. Add a new Action method to the CustomerController and name it “Remove”. Make sure it takes an ID of type string as an argument. Use LINQ to SQL to remove a Customer with the CustomerID matches the value of the ID argument. To Remove a Customer you first need to retrieve the Customer object, and then use the method DeleteOnSubmit method of the Customers property of the NorthwindDataContext object. The DeleteOnSubmit takes a Customer object as an argument. To execute the Delete, you need to call the SubmitChanges method of the NorthwindDataContext object.

    Note: Both the View method and the Remove method will get a specific Customer by CustomerID, so you can do some refactoring here and use Extract method to extract code which will get the Customer by CustomerID and put it into a new method, and reuse that method both in the View and Remove Action method. To use the Refactoring feature within Visual Studio, mark the code you want to extract to a method and right click on the selected code block. Select Refactoring and Extract method option. Give the new method a name and press the OK button.

    If you decide to use the Refactoring feature you can mark the following code in the View method:


    Customer
    customer = (from c in northwindDataContext.Customers where c.CustomerID == ID select c).FirstOrDefault(); if (customer == null) throw new ApplicationException( String.Format("Can't find a Customer with the CustomerID {0}", ID));


    Righ click on the selected code and select Refactoring and Extract method. Give it the name “GetCustomerByID” and press the OK button. If you get an alert box, just press “Yes” to ignore it.

    You should now have a new method with the name GetCustomerByID which the View method will make a call to.

    In the Remove method, make a call to the GetCustomerByID to get the Customer object to delete. If the application fails to remove a Customer, throw an ApplicationException, for example with the following message: “Can’t remove the Customer <customerID>”. If the remove was successfully, make a call to RedirectToAction to redirect to the Action with the name “List”. This will make sure the List method of the CustomerController will be called.


    public void
    Remove(string ID) { Customer customer = GetCustomerByID(ID); northwindDataContext.Customers.DeleteOnSubmit(customer); try { northwindDataContext.SubmitChanges(); RedirectToAction("List"); } catch (Exception e) { throw new ApplicationException(
    string.Format("Can't remove the Customer {0}", ID), e); } }


    16. Next step is to add a HyperLink to the Customers View, and set the URL to “/Customer/Remove/<CustomerID>” for each Customer. The name of the HyperLink should be “Remove” and can be placed before the CustomerID HyperLink. When you press the HyperLink, the Remove method of the CustomerController will be called and it will remove the Customer.


    <%
    @ Page Language="C#" AutoEventWireup="true" CodeBehind="Customers.aspx.cs" Inherits="CustomerApp.Views.Customer.Customers" %> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" > <head runat="server"> <title></title> </head> <body> <div> <table> <% foreach ( var customer in ViewData) { %> <tr> <td> <a href="/Customer/Remove/<%=customer.CustomerID %>">Remove</a>&nbsp; <a href="/Customer/View/<%=customer.CustomerID %>"><%=customer.CustomerID %></a> </td> <td><%=customer.ContactName %></td> </tr> <% } %> </table> </div> </body> </html>


    Build the project and run the application, enter the URL to list Customers (By now you should know the URL). Press one of the Remove HyperLink. You will probably get a SQL Exception about reference problems. You aren’t going to resolve that exception. It will indicate that the remove was executed and in this lab you can be satisfied with that.

     

    17. Add an Update method to the CustomerController, make sure it takes a argument with the name customerID and of type String. Use the customerID to call the GetCustomerByID method to retrieve the Customer from the database. Update the Customer with the new information and use the RedirectToAction method to redirect to the “List” action.

    Note: When using LINQ to SQL we need to get a Customer which is attached to a DataContext, which is the reason why you need to get the Customer. Another reason is also to make it easy to handle concurrency issues etc.

    Use the BindingHelperExtension class’s UpdateFrom method to set the Customer objects properties out from the Reuqest.Form collection.

    Note: The BindingHelperExtension’s UpdateFrom method will get the value out from an input field with the same name as the property of the object passed to the UpdateFrom method and set the value to the object. In the Customer View, there were input fields with the same name as the properties of the Customer object, so those values will be “mapped” to the Customer object you will pass into the UpdateFrom method.


    public void
    Update(string customerID) { Customer customer = GetCustomerByID(customerID); BindingHelperExtensions.UpdateFrom(customer, Request.Form); northwindDataContext.SubmitChanges(); RedirectToAction("List"); }


    Note: Values of input fields on a View which has the same name as the arguments of the Update action method will be passed as a value to the respective argument. So in this case you have an input hidden field with the name CustomerID on the View, the value of that field, will be passed as a value to the Update method. So instead of using the UpdateFrom method of the BindingHelperExtensions, you could use arguments for all input fields.

    If you want to learn more about the BindingHeperExtension class, you can read my post "ASP.Net MVC Framework 2 - The BindingHelperClass (UI-mapper)".

     

    18. Open the Customer View and change the <form> element’s action argument to call the Update method of the CustomerController “/Customer/Update”. Add a Submit button to the View and give it the text value “Update”.

     

    <%@ Page Language="C#" AutoEventWireup="true" CodeBehind="Customer.aspx.cs" Inherits="CustomerApp.Views.Customer.Customer" %>
    
    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
    
    <html xmlns="http://www.w3.org/1999/xhtml" >
    <head runat="server">
        <title></title>
    </head>
    <body>
        <form method="post" action="/Customer/Update">
            <table>
                <tr>
                    <td>Customer ID:</td>
                    <td><%= ViewData.CustomerID %></td>
                </tr>
                <tr>
                    <td>Company name:</td>
                    <td><input type="text" name="CompanyName" value="<%= ViewData.CompanyName %>" /></td>
                </tr>
                <tr>
                    <td>Contact Name:</td>
                    <td><input type="text" name="ContactName" value="<%= ViewData.ContactName %>" /></td>
                </tr>
                <tr>
                    <td>Phone:</td>
                    <td><input type="text" name="Phone" value="<%= ViewData.Phone %>" /></td>
                </tr>
    
            </table>
    
            <input type="hidden" name="CustomerID" value="<%= ViewData.CustomerID %>" />
            
            <input type="submit" value="Update" />
        </form>
    </body>
    </html>
    

     

    Build the solution and run the application. Enter the URL to list all Customers “Customer/List” and select a Customer from the list. Update the Customer and press the Update button. When the Update button is pressed, the customer will be updated and you will be redirected to the List action so the List of Customer will be displayed.

    Note: Instead of using an input field with the name CustomerID, you could also set the action attribute to “/Customer/Update/<the customerID>”.

    The last thing to do now is to create an ActionFilter for the Remove action method. You only want to make sure the user with the name “student” will make a call to the Remove action method.

     

    19. Add a new class to the Model folder. Name it “AuthorizedFilterAttribute”. Make sure it inherits from the base class ActionFilterAttribute. The ActionFilterAttribute is located in the namespace “System.Web.Mvc”.

    The ActionFilter can be used to perform pre- and post action. You can use FilterAction to cancel an execution of an Action method, or you can use it to log information etc. The ActionFilter AuthorizedFilter will be used to only make sure the user “student” has access to the Remove method, if not a SecurityException will be thrown.

    To implement code for a pre action, override the OnActionExecuting method of the ActionFilterAttribute class. To perform a post action, override the OnActionExecuted method. To get the current user you can use the filterContext arugment’s HttpContext property and its User.Identity.name property. You don’t need to cancel the action of the AuthorizedFilter when you throw an exception. To cancel, you can set the filterContext Cancel property to true.


    using
    System.Security; namespace CustomerApp.Models { public class AuthorizedFilter : ActionFilterAttribute { public override void OnActionExecuting(FilterExecutingContext filterContext) { if (filterContext.HttpContext.User.Identity.Name != "student") throw new SecurityException(
    "You aren't allowed to call this action!"); } } }


    Apply the AuthorizedFilter to the Remove method.


    [AuthorizedFilter] public void Remove(string ID)


    Note: If you want to make sure the filter will be used for every Action method of a Controller, you can add the attribute to the Controller’s class.

    If you want to test if the filter works, add a breakpoint to the OnActionExecuting method and run the application in debug mode. List the Customers and press the Remove HyperLink, or you can simply run the application, list the customers and press the Remove Hyperink, it should probably give you a SecurityException. If you want to learn more about ActionFilter, you can read my post "ASP.Net MVC Framework 2 - Interception and ceating a Role Action Filter".

    In this step by step guide you have learned how to create a simple application by using the ASP.Net MVC Framework. You have learned how to create a Controller, Views and also ActionFilter to intercept pre actions to a Action method.

  • ASP.Net MVC Framework - Using Forms Authentication

    This post is about using Forms Authentication with the MVC Framework. I only want to let you know that this is a simple and fast “hack” only to show a simple solution to get it work. I decided to use the Membership feature for the validation of user name and password.

    The first thing we need to do is to enable Forms Authentication and specify a Login Url, I decided to use the Login URL in a MVC “friendly” format:

     

    <authentication mode="Forms">
       <forms loginUrl="Login/Login"></forms>
    </authentication>

     

    The value of the loginUrl has some redundant value, Login/Login, but this is because the first Login points to the LoginController and the last Login to the Action method Login in the LoginController.

    The next thing we need to do is to make sure anonymous users don’t have access to our site, this is done by adding the authorization section to the web.config:

     

    <authorization>
         <deny users="?"/>
    </authorization>

     

    If a user now try to get access to our site, he/she will be redirected to the ”Login/Login” URL, which will by using the default URL format in the RouteTable:


    Url = "[controller]/[action]"


    Call the Login Action method of the LoginController.

    In the Views folder we can add our Login View (We will look at the LoginController later in this post).


    /Views/Login/Login.aspx


    The following is a simple Login View:

     

    <%@ Page Language="C#" AutoEventWireup="true" CodeBehind="Login.aspx.cs" Inherits="MvcApplication4.Views.Login.Login" %>

    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

    <html xmlns="http://www.w3.org/1999/xhtml" >

    <head runat="server">
        <title>Untitled Page</title>
    </head>

    <body>
        <div>
            <% if (this.ViewData.ContainsDataItem("LoginFaild")) { %>
            <div style="color:Red"><%=this.ViewData["LoginFaild"] %></div>
            <% } %>

            <form method="post" action="/Login/Login">
            User name: <input type="text" name="userName" /><br />
            Password:&nbsp; <input type="password" name="password" /><br /><br />
            <input type="submit" value="Login" />
            </form>

        </div>

    </body>
    </html>

     

    If the ViewData in will contain an data item with the name LoginFaild it will display the value added to the LingFaild data item, in this case a message like “Login faild….”

    The <form> element’s action method will make sure the LoginController’s Login method will be called when the user hit the Login submit button. As you can see the same URL specified for the FormsAuthentication’s loginUrl is used here. It’s because the <location> section in the web.config will not work with the MVC Framework, so if we try to use another URL like “/Login/Authenticate” etc, we will be redirected to the “Login/Login” URL because the <authorization> section denies all anonymous user to access any URL in our app, besides the specified loginUrl.

    The username and password input fields values will be passed as arguments to the Login action of the LoginController. Here is the implementation of the LoginController and its Login action:

     

        public class LoginController : Controller
        {
            [ControllerAction]
            public void Login(string userName, string password, string ReturnUrl)
            {
                if (this.IsValidLoginArgument(userName, password))
                {
                    if (Membership.ValidateUser(userName, password))
                        this.RedirectFromLoginPage(userName, ReturnUrl);
                    else
                        this.ViewData["LoginFaild"] = "Login faild! Make sure you have entered the right user name and password!";
                }

                RenderView("Login");
            }

            private void RedirectFromLoginPage(string userName, string ReturnUrl)
            {
                FormsAuthentication.SetAuthCookie(userName, false);

                if (!string.IsNullOrEmpty(ReturnUrl))
                    Response.Redirect(ReturnUrl);
                else
                    Response.Redirect(FormsAuthentication.DefaultUrl);
            }

            private bool IsValidLoginArgument(string userName, string password)
            {
                return !(string.IsNullOrEmpty(userName) && string.IsNullOrEmpty(password));
            }
        }

     

    As you can see the Login action method takes three arguments, the userName, password and ReturnUrl. The value for the userName and password, will came from the input fields located on the Login View, the ReturnUrl is a query string used by the FormsAuthentication, so the value of the ReutnrUrl argument will came from the ReturnUrl query string. If we have input fields with the same name as the arguments of an action method in the View, the value of the fields will be “mapped” to the argument of the action method with the same name as the field, the same thing with query strings.

    Note: There is a bug in the current CTP of the MVC Framework, so the value of a query string will not always be passed to the action method’s arguments.

    The firs lines of code in the Login action method will check if the username and password aren’t empty. The check is used to make sure the Login View will be displayed when FormsAuthentication will redirect to the LoginURL “Login/Login”. If a user tries to enter and URL and the user is an anonymous user (Not authenticated), the Login Action method of the LoginController will be executed. In this case the username and password argument will be null, and the Login View will be rendered. When the Login view is displayed, the user can now enter a username and a password and hit the Login button. When he/she have done that, the LoginController’s Action method will be called, and this time the username and password will not be empty, so the Membership feature will validate the user, if he/she is valid, the Authentication Cookie used by the FormsAutehtnicon will be set, and the user will be redirected to the URL he/she have requested, and the user is now authenticated and have access to the site.

    If the user enter a invalid user name or password, the Login View will be displayed and the “Login faild!.....” text added to the ViewData will be displayed.

    This was a simple solution to use the Forms Authentication, there are some other options where can simply create our own RouteHandler which can handle the authentication check and redirect to a login View, or we can handle it on a Controller level. I will probably write a blog post about that later.

    I have another post about the MVC Framework and security: http://weblogs.asp.net/fredriknormen/archive/2007/11/25/asp-net-mvc-framework-security.aspx. The post is created on the pre-CTP of the MVC Framework, so have that in mind, but basically the solution is the same.

  • ASP.Net MVC Framework - Assign a ViewFactory to a Controller

    I have seen several posts about how to assign a ViewFactory to a Controller, most of the case the code looks like this:


    public class HomeController : Controller
    {
        public HomeController()
        {
           this.ViewFactory = new MyViewFactory();
        }
    }

    The ViewFactory is assigned into the constructor of a Controller. If we want to use the same ViewFactroy for all Controllers in out app, we can for example create a new base class for our Controller:


    public class MyController : Controller
    {
         public MyContoroller()
         {
             this.ViewFactory = new MyViewFactory();
         }
    }

    public class HomeController : MyController
    {
         public HomeController() : base()
         {
         }
    }


    I’m a more fan of making this configurable via a configuration file, to map a ViewFacotry to a Controller, this can for example be done with Spring.Net:


    <?xml version="1.0" encoding="utf-8" ?>
    <objects xmlns="http://www.springframework.net"
                    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
                    xsi:schemaLocation="http://www.springframework.net http://www.springframework.net/xsd/spring-objects.xsd">

      <object id="HomeController" type="MvcApplication.Controllers.HomeController, MvcApplication, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null">
        <constructor-arg name="viewFactory" ref="MyFactory"/>
      </object>

      <object id="MyFactory" type="MyViewFactory, MvcApplication, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null"/>
     
    </objects>


    Note: You can read my other post about using Spring.Net to create Controllers.


    public class HomeController : Controller
    {
       public HomeController() : this(new DefaultViewFactory ()) {}
       public HomeController(IViewFactory viewFactory)
       {
           this.ViewFactory = viewFactory;
       }
     }


    There a so many different solutions to set a ViewFactory during creation of a Controller, for example by creating an own ControllerFactory, the following code is from my post about writing your own IRoutHandler:


    public IController CreateController(RequestContext context, string controllerName)
     {
            //I should refactor out the following two lines to not only make sure to make the creation of the ObjectFactory once.
            IResource input = new FileSystemResource(context.HttpContext.Request.MapPath("objects.xml"));
            IObjectFactory factory = new XmlObjectFactory(input);

            IController controller = (IController)factory.GetObject(controllerName, typeof(IController));

            if (typeof(Controller).IsAssignableFrom(controller.GetType()))
            {
              N2MVCConfigurationHandler config = ConfigurationManager.GetSection("Nsquared2/N2MVCSection") as N2MVCConfigurationHandler;
              ((Controller)controller).ViewFactory = N2ViewFactoryBuilder.GetViewFactory(config.ViewFactory);
            }

            return controller;
    }


    What this code does, is to see if the Controller is assigned from the Controller class, ugly code, I know, but it’s only the Controller base class that has the ViewFactory property, and we can create a Controller which only implements the IController interface, and this interface don’t have the ViewFactory property.

    The code above will get the ViewFactory from a configuration file:


    <Nsquared2>
        <N2MVCSection
            controllerFactory="MvcApplication.Models.N2ControllerFactory, MvcApplication, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null"
            viewFactory="MvcApplication.Models.N2ViewFactory, MvcApplication, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null"/>
    </Nsquared2>


    In this example I assume that every Controller use the same ViewFactory, and that may not always be the case, so in that case the earlier example by using Spring.Net an inject a ViewFactory would be better use, or creating a new ControllerFactory which will map a Controller to a ViewFactory, so when the ControllerFactory creates the Controller, the factory could look up in a config file to see, which ViewFactory should be used for the specific Controller, for example:


    <Controllers defaultViewFactory=”myViewFactory”>
        <add name=”HomeController” ViewFactory=”myOwnViewFactory” …>
    </Controllers>


    What is the main goal with these kinds of solutions? Well we can now simply change the ViewFactory for our Controller without recompiling our code, and we will also remove the dependency from a Controller and the ViewFactory by not hard code the instantiation within the Controllers. I prefer Interface Driven Programming and the use of factories, and the possibility to easy configure the use of classes etc in a configuration file. That is why I also loved the Provider based solution that was shipped with ASP.Net 2.0, like the Membership and Roles features etc.

  • ASP.NET MVC Framework - Ajax

    Take look at Nikhil's blog about how AJAX can be used with the ASP.Net MVC Framework:

    http://www.nikhilk.net/Ajax-MVC.aspx

  • ASP.Net MVC Framework - Handling Exception by using an Attribute

    Note: This is based on the pre-CTP version of the ASP.Net MVC Framework and it is in an early stage.

    I got a comment on one my previous post “ASP.Net MVC Framework - Exception Handling” that the Rescue style MonoRail is a nice solution to handle Exception thrown within a Controller. In this post I have added something similar to as the Rescue feature of MonoRail (at least I think so ;)) where an attribute can be added to a Controller or on an Action. The attribute can be used to specify which View that should be rendered when an exception is thrown. We can also specify different Views based on different exceptions. Something I also added to the code is to specify something I will call an ExceptionHanlder. The ExceptionHandler is a pre handler which will be executed before the specified exception View should be rendered. The idea of the ExceptionHandler is to log exception etc.

    By using an Attribute (ExceptionHandlerAttribute) on a Controller or an Action method, we don’t need to override the OnError method of the base Controller class. I made this solution simple by only creating my own custom Controller class (N2Controller) and override the OnError method of the Controller. In this method do my exception handling process. Before I show the code I will show you how we can use the ExceptionHandlerAttribute.

    The ExceptionHandlerAttribute has three argument, exceptionHandler, viewName, exceptionType. The exceptionHandler argument is where we pass the type of the pre ExceptionHandler we want to use when an Exception is thrown. The viewName is the name of the View to render the exception information (In this sample code the view specified must be located in the Views/Exceptions folder). The exceptionType takes a type of an Exception. If we only want to make sure the Exception Handler should only handle a specific exception thrown within the Controller or Action method, we use the exceptionType argument.

    Here is an example of different use of the ExcetpionHandlerAttribute, it can be used on both the Controller’s Class and Action methods:

    1. [ExceptionHandler(“Error”);
    2. [ExceptionHandler("Error", typeof(DivideByZeroException))]
    3. [ExceptionHandler(typeof(ExceptionHandler), "Error")]
    4. [ExceptionHandler(typeof(ExceptionHandler), "Error", typeof(DividedByZeroException))
    5. [ExceptionHandler(typeof(ExceptionHandler))
    6. [ExceptionHandler(typeof(ExceptionHandler), typeof(DividedByZeroException))

    1 Will who the Error view when an exception is thrown.
    2 Will only show the Error View is the exception thrown is of type DividedByZeroException.
    3 Will Show the Error View and use a pre ExceptionHandler before the View is rendered.
    4 Will Show the Error View only if the DividedByZeroException is thrown, and use a pre ExceptionHandler before the View is rendered.
    5 Will use the ExceptionHandler when an exception is thrown.
    6 Will use the ExceptionHandler if the DividedByZeroException is thrown.

    To use ExceptionHandler we simply create a Class that implements the IExceptionHandler interface.

    public interface IExceptionHandler
    {
        void PreExceptionHandling(string actionName, Exception exception);
    }

    In the PreExceptionHandling method we could for example put logic to log an exception. The PreExceptionHandling method will be called before a View will be rendered (A View will not be rendered if it’s not specified).

    Here is an example how the ExceptionHandlerAttribute can be used:

    [ExceptionHandler("DefaultError", typeof(DivideByZeroException))]   <- Will not be used
    public class HomeController : N2Controller
    {
       [ControllerAction, ExceptionHandler("Error")]  <- Will be used
       public void Index()
       {
        
       }
    }

    The attribute specified on the action method will have more to sat than the attribute on the Controller’s class.

    [ExceptionHandler("DefaultError", typeof(DivideByZeroException))]   <- Will be used
    public class HomeController : N2Controller
    {
        [ControllerAction, ExceptionHandler("Error",typeof(IndexOutOfRangeException),typeof(ArgumentException))]  <- Will be used
        public void Index()
        {
            ...
        }
    }

    In the code above, both Exception Handler will be used. For example, if the Action method throws a DidivedByZeroException the DefaultError View will be rendered. But if the IndexOurOfRangeException or ArgumetnException is thrown the Error View will be displayed.

    Here is the implementation of my custom Controller:

    public class N2Controller : Controller
    {
        protected override bool OnError(string actionName, System.Reflection.MethodInfo methodInfo, Exception exception)
        {
            ArrayList attributes = this.GetExceptionHandlerAttribute(methodInfo);

            foreach (ExceptionHandlerAttribute attribute in attributes)
            {
               foreach (Type exceptionType in attribute.ExceptionTypes)
               {
                  if (exceptionType.IsAssignableFrom(exception.InnerException.GetType()))
                  {
                     if (attribute.ExceptionHandler != null)
                        ((IExceptionHandler)Activator.CreateInstance(attribute.ExceptionHandler)).PreExceptionHandling(actionName, exception);

                     if (!string.IsNullOrEmpty(attribute.View))
                         RenderView("~/Views/Exceptions/" + attribute.View + ".aspx", exception);
                        
                     return true;
                  }
               }
           }

           return false;
       }

       private ArrayList GetExceptionHandlerAttribute(System.Reflection.MethodInfo methodInfo)
       {
           ArrayList attributes = new ArrayList();

           attributes.AddRange(
                               methodInfo.GetCustomAttributes(
                                     typeof(ExceptionHandlerAttribute),
                                     false));

           attributes.AddRange(
                               this.GetType().GetCustomAttributes(
                               typeof(ExceptionHandlerAttribute),
                               true));
                
           return attributes;
        }
    }

    The code above is only on a prototype stage and. If no ExceptionHandlerAttribute is specified for an Action or Controller, the original ASP.Net MVC Frameowork’s OnError behavior will be used.

  • ASP.Net MVC Framework - Create your own IRouteHandler

    In my previous post I wrote about how we can create our own IControllerFactory for the ASP.Net MVC Framework; in this post I will show you how we can create our own IRouteHandler.

    The RouteHandler in this post will replace the IControllerFactory with my own Controller Factory, and also set a default ControllerFactory and a ViewFactory specified in the web.config. This is something the current preview bits of the MVC Framework can’t.

    It’s quite easy to create our own RouteHandler, we only need to implement the IRouteHandler interface and implement the GetHttpHandler method ;)

    public class N2MVCRouteHandler : IRouteHandler
    {
       public IHttpHandler GetHttpHandler(RequestContext requestContext)
       {
           N2MVCHandler handler = new N2MVCHandler();
           handler.RequestContext = requestContext;
           return handler;
        }
    }

    The GetHttpHandler returns an IHttpHandler. I have created my own IHttpHandler (N2MVCHandler). The N2MVCHandler inherits the MvcHandler shipped with the ASP.Net MVC Framework. The reason why I inherit the MvcHandler is because I don’t want to rewrite stuff that isn’t necessarily. In my IHttpHandler I override the ProcessRequest method and added my own code to create a Controller for the current request.

    public class N2MVCHandler : MvcHandler
    {
        protected override void ProcessRequest(IHttpContext httpContext)
        {
           if (this.RequestContext == null)
              throw new InvalidOperationException("No RequestContext");

           string controllerName = this.GetRequiredString(this.RequestContext.RouteData, "controller");

           IController controller = this.CreateController(controllerName);
           ControllerContext controllerContext = new ControllerContext(base.RequestContext, controller);

           controller.Execute(controllerContext);
        }

        private IController CreateController(string controllerName)
        {
           N2MVCConfigurationHandler config = ConfigurationManager.GetSection("Nsquared2/N2MVCSection") as N2MVCConfigurationHandler;

           IN2ControllerFactory controllerFactory = N2ControllerFactoryBuilder.GetControllerFactory(config.ControllerFactory);

           IController controller = controllerFactory.CreateController(base.RequestContext, controllerName);

           return controller;
        }
    }

    In the ProcessRequest method I also create a Controller for the current request by calling the CreateController method. The CreateController method in the N2MvcHandler instantiates a ControllerFactroy which is specified in the web.config file. When the factory is instantiated I make a call to its CreateController method to create an instance of the Controller.

    In this example I have replace the IControllerFactory with my own interface, the reason is that I want to pass the name of the Controller and not the Type. The MvcHandler shipped with the MVC Framework will in the ProcessRequest method try to locate the Controller within the references assemblies and create the Type of the Controller and pass it to the IController.CreateController method. But I think it’s up to the ControllerFactory to look up the Controller. Here is my IControllerFactory interface:

    public interface IN2ControllerFactory
    {
        IController CreateController(RequestContext context, string controllerName);
    }

    Note: You can still create your own ControllerFactory, but you need to implement my interface instead of IControllerFactory, and you don’t need to make a call to the ControllerBuilder.Current.SetDefaultControllerFactory method in the Application_Start event in Global.asax to specify which ControllerFactory you want to use, instead you can do it in web.config. You will see how to do it later in this post.

    I decided to use Spring.Net in this post also to create my Controller within the ControllerFactory.

    public class N2ControllerFactory : IN2ControllerFactory
    {
        public IController CreateController(RequestContext context, string controllerName)
        {
            IResource input = new FileSystemResource(context.HttpContext.Request.MapPath("objects.xml"));
            IObjectFactory factory = new XmlObjectFactory(input);

            IController controller = (IController)factory.GetObject(controllerName, typeof(IController));

            if (typeof(Controller).IsAssignableFrom(controller.GetType()))
            {
              N2MVCConfigurationHandler config = ConfigurationManager.GetSection("Nsquared2/N2MVCSection") as N2MVCConfigurationHandler;
              ((Controller)controller).ViewFactory = N2ViewFactoryBuilder.GetViewFactory(config.ViewFactory);
            }

            return controller;
        }
    }

    It's the ControllerFactory's responsibility to create and fill the Controller with all it information it needs. In this case the ViewFactory.

    The IViewFactory is used to create a factory which has the responsibility to create a View. Because a Controller don’t need to implement the Controller base class I will in my code use a "code policy". I will check if the created Controller inherits the Controller class, if so I will create a IViewFactory and inject it to the Controller. The IController interface don’t have the ViewFactory property, it’s something we will get from the Controller base class. 

    If we take a look again at the CreateContoller method in the IHttpHandler (N2MVCHandler), we can see how I get the ControllerFactory from the web.config

    private IController CreateController(string controllerName)
    {
        N2MVCConfigurationHandler config = ConfigurationManager.GetSection("Nsquared2/N2MVCSection") as N2MVCConfigurationHandler;

        IN2ControllerFactory controllerFactory = N2ControllerFactoryBuilder.GetControllerFactory(config.ControllerFactory);

        IController controller = controllerFactory.CreateController(base.RequestContext, controllerName);

        return controller;
    }

    I use some other helper classes in my code to create an instance of the Controller- and ViewFactory specified in the web.config, the code of the helper methods is not relevant for this post. When the public CTP of the MVC Framework is released, you can drop me an e-mail and I can send you my source code.

    Here is the config section in the web.config where a conrollerFactory is specified and also a viewFactory. So we can now easy specify our ControllerFactory and ViewFactory in web.config.

    <Nsquared2>
        <N2MVCSection
            controllerFactory="MvcApplication.Models.N2ControllerFactory, MvcApplication, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null"
            viewFactory="MvcApplication.Models.N2ViewFactory, MvcApplication, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null"/>
    </Nsquared2>

    By using the Route objects’ RouteHandler, we can easy change the RouteHandler the MVC Framework should use.

    RouteTable.Routes.Add(new Route
    {
        Url = "[controller]/[action]/[id]",
        Defaults = new { action = "Index", id = (string)null },
        RouteHandler = typeof(N2MVCRouteHandler)
    });

    In this post you have seen how we can create our own IRouteHandler, and also replace the ControllerFactory with our own.

  • ASP.Net MVC Framework - Create your own IControllerFactory and use Spring.Net for DI

    In this post I’m going to show you how we can easy create our own IControllerFactory for the ASP.Net MVC Framework and use Spring.Net to create our Controllers. I will in this post also use the Spring.Net Dependency Injection support to pass a Repository that should be used by our Controller.

    To create a ControllerFactory we need to implement the IControllerFactory interface. This interface has one method, CreateController. This method takes two arguments a RequestContext and the Type of the controller to create. The MVC Framework will locate our Controller and passed in the Type to our IControllerFactory. This is something I don’t like; I want to be responsible to look up my own Controller in my way, not let the MVC Framework locate the type in their way, It will only creates some magic. If we want to change this behavior in the current preview of the MVC Framework we can create our own RouteHandler, but this is out of topic in this post. When we use Spring.Net to get an instance of an object we use a string, so we can use the Name property or the Type passed as an argument to our CreateController method to get the name of the Controller.

    Here is the implementation of an IControllerFactory which will use Spring.Net to instantiate a Controller:

    public class SpringControllerFactory : IControllerFactory
    {
            public IController CreateController(RequestContext context, Type controllerType)
            {
                IResource input = new FileSystemResource(context.HttpContext.Request.MapPath("objects.xml"));
                IObjectFactory factory = new XmlObjectFactory(input);

                return (IController)factory.GetObject(controllerType.Name);
            }
     }

    In this code I use the XmlObjectFactory to create objects out from a specified XML file, in this case the "objects.xml" file. Within the objects.xml file we can specify the objects we want to be instantiated when we are using the Spring.Net framework. I will later in this post show the content of the "objects.xml" file. But first we can take a look at the implementation of a Controller.

    public class HomeController : Controller
    {
            IHomeRepository _homeRepository;

            public HomeController() : this(new HomeRepository()) {}

            public HomeController(IHomeRepository homeRepository)
            {
                this._homeRepository = homeRepository;
            }

            [ControllerAction]
            public void Index()
            {
                CompanyInfo companyInfo = this._homeRepository.GetCompanyInfo();
                RenderView("Index", companyInfo);
            }

            [ControllerAction]
            public void Contact()
            {
                CompanyInfo companyInfo = this._homeRepository.GetContact();
                RenderView("Contact", companyInfo);
            }

            [ControllerAction]
            public void About()
            {
                CompanyInfo companyInfo = this._homeRepository.GetCompanyInfo();
                RenderView("About", companyInfo);
            }
     }

    I decided to modify the HomeController created by the "MVC Web Application" template. My modification of the HomeController was to remove the code to fill a CompanyInfo object, and instead put it into a Repository with the name "HomeRepostory".

    public class HomeRepository : IHomeRepository
    {
            public CompanyInfo GetCompanyInfo()
            {
                CompanyInfo companyInfo = new CompanyInfo();
                companyInfo.CompanyName = "Your company name here";
                return companyInfo;
            }

            public CompanyInfo GetContact()
            {
                CompanyInfo companyInfo = new CompanyInfo();
                companyInfo.CompanyName = "Your company name here";
                companyInfo.AddressLine1 = "Company address Line 1";
                companyInfo.AddressLine2 = "Company address Line 2";
                companyInfo.City = "City";
                companyInfo.State = "State";
                companyInfo.Zip = "00000";
                companyInfo.Email = "
    email@yourcompany.com
    ";
               
                return companyInfo;
            }
     }

    The HomeRepository implements an interface with the name IHomeRepository. I made this design decision so I can easy mock my repositories. If we take a look at the HomeController again we can see that I have added a private field with the type IHomeRepository and also created a constructor which will take an IHomeRepository as an argument. The reason to this design is because of testability. For example if I mock the HomeRepository I can in my Test project easy inject the mock object for the Repository to my Controller.

    MockHomeRepository mockHomeRepository = new MockHomeRepository();
    HomeController homeController = new HomeController(mockRepository);

    homeController.About();

    Assert.AreEqual(((CompanyInfo)homeController.ViewData).companyInfo, "Your company name here");

    By using a constructor which will take our IHomeRepository as an argument, we can also do a Constructor Injection by using the Sprin.Net framework, and that is exactly what I’m going to do in this post.

    Now when we have our Controller and Repository we need to setup the “objects.xml” file with our objects and also setup a Constructor Injection. Here is the content of the “objects.xml” file:

    <?xml version="1.0" encoding="utf-8" ?>
    <objects xmlns="
    http://www.springframework.net"
                    xmlns:xsi="
    http://www.w3.org/2001/XMLSchema-instance"
                    xsi:schemaLocation="
    http://www.springframework.net http://www.springframework.net/xsd/spring-objects.xsd">

      <object id="HomeController" type="MvcApplication.Controllers.HomeController, MvcApplication, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null">
        <constructor-arg name="homeRepository" ref="HomeRepository"/>
      </object>

      <object id="HomeRepository" type="MvcApplication.Models.Repositories.HomeRepository, MvcApplication, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null"/>
     
    </objects>

    The Object element specifies our objects we want to create with the Spring.Net framework. The id attribute specifies the name of the object and the type specifies the type of our object. When we use the Spring.Net framework’s GetObject method we pass in the name of the object to create, the GetObject method will look for an object with the id attribute set to the name, and instantiate the specified type. By using the <constructor-arg> element we can specify what object that should be passed into our constructor when we create the object. In this case I have specified the HomeRepository object.

    When our IControllerFactory now will create our HomeController, Spring.net will create an instance of our Controller and do a constructor injection and pass in the HomeRepository object to the constructor and return our controller.

    Now when we are done with all the implementation of our IControllerFactory, Controller and Repository we need to make sure our IControllerFactory should be used. This can be done in the Global.asax’s Application_Start event by using the SetDefaultControllerFactory method:

    protected void Application_Start(object sender, EventArgs e)
    {
       ControllerBuilder.Current.SetDefaultControllerFactory(
                               typeof(MvcApplication.Models.Infrastructure.SpringControllerFactory));
       ...
    }

    When we now run our application, our IControllerFactory will be used and all the creating of our Controllers will be handled by the Spring.Net framework.


Den här bloggen

Syndication