In my last post I wrote about why you should take a look at ASP.NET MVC. With this blogpost I want to go a little bit deeper into the MVC universe.
The project template
After the installation of ASP.NET MVC (currently beta) you will find a new project template in Visual Studio:
Right after you click ok you get another dialog:
ASP.NET MVC is very testable and ask you if you want to create a unit test project. I recommend you to do that (or create your own unit test project) - unit tests are a great way to produce high quality.
The default test framework is MSTest, other frameworks will be added later (hopefully :) ).
Project structure:
Now you should see a solution like this (i added another project to this solution, but you don´t need to care about it):
The "ReadYou.WebApp" shows the typical ASP.NET MVC folder structure:
- Controller: Logic
- Models: Business data / application model
- Views: simple ASPX / ASCX / masterpage (or other) views
Views, Controller & Models in detail
The project template include 2 controller and 3 view folders:
- The "Shared" folder contains all viewelements that could be used by other views, like the masterpage or some common controls.
- Each controller has it´s own view folder ("AccountController" - "Account" / "HomeController" - "Home")
- This "path"-configuration can be modified by using the diverent interfaces, hier are 2 good examples "Partitioning an ASP.NET MVC application into separate "Areas"" and Rob Conerys version.
- The "model" folder contains only the application data classes (normal CRL classes)
A short look on the default website:
The default MVC website is great to get a first look how MVC is working. There is masterpage, a simple membershipsystem and some simple form stuff (the login and register page) :
The request Flow:
Justin Etheredge create a great overview of the request flow and where the extensibility points are: ASP.NET MVC Request Flow
Communication between the controller and the view:
You can use the "ViewData"-dictionary to send data from the controller (the request will be routed to an action method of a controller) :
- public ActionResult Index()
- {
- ViewData["Title"] = "Home Page"; </span>
- ViewData["Message"] = "Welcome to ASP.NET MVC!"; </span>
- return View(); </span>
- }
The data in the dictionary will be send to a view called "Index" in the "Home" folder, because the action method is a method of the "HomeController". You can specify a view in the "View(YOURVIEW)" method, but if you just use the "View()" method the MVC framework will route the data to a view namend like the action method itself e.g. "Index".
Source code of the "Index.aspx" (~/Views/Home/):
- <%@ Page Language="C#" MasterPageFile="~/Views/Shared/Site.Master" AutoEventWireup="true" CodeBehind="Index.aspx.cs" Inherits="ReadYou.WebApp.Views.Home.Index" %>
- <asp:Content ID="indexContent" ContentPlaceHolderID="MainContent" runat="server"> </span>
- <h2><%= Html.Encode(ViewData["Message"]) %></h2> </span>
- <p>
- To learn more about ASP.NET MVC visit <a href="http://asp.net/mvc" title="ASP.NET MVC Website">http://asp.net/mvc</a>. </span>
- </p>
- </asp:Content>
The data in the "ViewData"-dictionary are rendered though the inline code. There is no code behinde file, it´s similar to PHP/JSP or classic ASP.
As you can see: ASP.NET MVC is still MVC. You can still use the masterpage files and the content placeholder and use the "ViewData" in the masterpage:
- ...
- <head runat="server"> </span>
- <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" /> </span>
- <title><%= Html.Encode(ViewData["Title"]) %></title> </span>
- </head>
- ...
Strongly typed ViewData:
The dictionary isn´t very great in bigger applications, but you can use a strongly typed ViewData class to send data from the controller to the view. If you take a look at the code behinde file of the viewpage, than you see a partial class which is inherited from ViewPage:
- namespace ReadYou.WebApp.Views.Home
- {
- public partial class Index : ViewPage </span>
- {
- }
- }
You can pass in your own "ViewData" class to the ViewPage<T>:
Index.aspx.cs:
- namespace ReadYou.WebApp.Views.Home
- {
- public class IndexViewData </span>
- {
- public string Text { get; set; } </span>
- }
- public partial class Index : ViewPage<IndexViewData> </span>
- {
- }
- }
Index.aspx:
- <%@ Page Language="C#" MasterPageFile="~/Views/Shared/Site.Master" AutoEventWireup="true" CodeBehind="Index.aspx.cs" Inherits="ReadYou.WebApp.Views.Home.Index" %>
- <asp:Content ID="indexContent" ContentPlaceHolderID="MainContent" runat="server"> </span>
- <h2><%= Html.Encode(ViewData.Model.Text) %></h2>
- <p>
- To learn more about ASP.NET MVC visit <a href="http://asp.net/mvc" title="ASP.NET MVC Website">http://asp.net/mvc</a>. </span>
- </p>
- </asp:Content>
HomeController.cs:
- public ActionResult Index()
- {
- ViewData["Title"] = "Home Page"; </span>
- IndexViewData data = new IndexViewData(); </span>
- data.Text = "Hi strongly typed viewdatat"; </span>
- ViewData.Model = data;
- return View(); </span>
- }
The benefit of this approach is, that you get a more robust application and have a "contract" between the view and the controller.
Warning: The "ViewData["Title"]" is still in use, because the masterpage needs this data. In the MVC universe the controller is responsible to send all data to the view (there are some ideas how you can create an get an independent control).
You finde more information in this great blogpost of Scott Guthrie (it´s an older one, but the concepts are still in use):
ASP.NET MVC Framework (Part 3): Passing ViewData from Controllers to Views
Communication between the view and the controller
The view talks to the controler via normal HTML links or forms (GET/POST data).
The template included some examples:
Option 1: Via GET or the "ActionLink"
If you want to go to the register page, you just click on this link:
You just use the HTML "ActionLink" helper:
- <p>
- Please enter your username and password below. If you don't have an account,
- please <%= Html.ActionLink("register", "Register") %>. </span>
- </p>
The request will be routed to the "Register" action method on the "AccountController", because this view is inside the "Account"-folder and the second ActionLink parameter contains the name of the specific action method.
Option 2: Via POST
The login mask (username / password) use a standard HTML form:
- <form method="post" action="<%= Html.AttributeEncode(Url.Action("Login")) %>">
- <div>
- <table>
- <tr>
- <td>Username:</td>
- <td><%= Html.TextBox("username") %></td> </span>
- </tr>
- <tr>
- <td>Password:</td>
- <td><%= Html.Password("password") %></td> </span>
- </tr>
- <tr>
- <td></td>
- <td><input type="checkbox" name="rememberMe" value="true" /> Remember me?</td> </span>
- </tr>
- <tr>
- <td></td>
- <td><input type="submit" value="Login" /></td> </span>
- </tr>
- </table>
- </div>
- </form>
The Url.Action Helper create the action URL for the form. The form values will be submitted to the "Login" action method of the "AccountController", beacuse this view is inside the "Account"-folder.
The source code of the "Login" action method:
- public ActionResult Login(string username, string password, bool? rememberMe)
- {
- ViewData["Title"] = "Login"; </span>
- // Non-POST requests should just display the Login form </span>
- if (Request.HttpMethod != "POST") </span>
- {
- return View(); </span>
- }
- // Basic parameter validation </span>
- List<string> errors = new List<string>(); </span>
- if (String.IsNullOrEmpty(username)) </span>
- {
- errors.Add("You must specify a username."); </span>
- }
- ...
- }
The form values are automatically mapped to the method parameters. This mapping could be modified. Stephen Walther wrote a nice blogpost about this. And there is a great screencast how the binding in ASP.NET MVC works on Dimecasts.net.
Many helpers are overloaded and take strongly typed data - just try it out :)
"Best Practices", tips and information
The MVC framework is currently a beta version, that´s why you need to search at different blogs or websites to learn more about the framework. Here are my recommendations:
- Stephen Walthers Blog
- Rob Conerys MVC Storefront
- Scott Hanselman
- Scott Guthrie
- ASP.NET MVC Seite
- ASP.NET MVC Forum
- ASP.NET MVC Videos
- DotNetKicks - MVC
Feedback
Feel free to comment this blogpost (and my english ;) )