ASP.NET MVC Framework (Part 1)

Two weeks ago I blogged about a new MVC (Model View Controller) framework for ASP.NET that we are going to be supporting as an optional feature soon.  It provides a structured model that enforces a clear separation of concerns within applications, and makes it easier to unit test your code and support a TDD workflow.  It also helps provide more control over the URLs you publish in your applications, and can optionally provide more control over the HTML that is emitted from them.

Since then I've been answering a lot of questions from people eager to learn more about it.  Given the level of interest I thought it might make sense to put together a few blog posts that describe how to use it in more detail.  This first post is one of several I'll be doing in the weeks ahead.

A Simple E-Commerce Storefront Application

I'm going to use a simple e-commerce store application to help illustrate how the ASP.NET MVC Framework works.  For today's post I'll be implementing a product listing/browsing scenario in it.

Specifically, we are going to build a store-front that enables end-users to browse a list of product categories when they visit the /Products/Categories URL on the site:

ASP.NET MVC Framework (Part 1)_第1张图片

When a user clicks on a product category hyperlink on the above page, they'll navigate to a product category listing URL - /Products/List/CategoryName -  that lists the active products within the specific category:

ASP.NET MVC Framework (Part 1)_第2张图片

When a user clicks an individual product, they'll navigate to a product details URL - /Products/Detail/ProductID - that displays more details about the specific product they selected:

ASP.NET MVC Framework (Part 1)_第3张图片

We'll build all of the above functionality using the new ASP.NET MVC Framework.  This will enable us to maintain a "clean separation of concerns" amongst the different components of the application, and enable us to more easily integrate unit testing and test driven development.

Creating A New ASP.NET MVC Application

The ASP.NET MVC Framework includes Visual Studio Project Templates that make it easy to create a new web application with it.  Simply select the File->New Project menu item and choose the "ASP.NET MVC Web Application" template to create a new web application using it.

By default when you create a new application using this option, Visual Studio will create a new solution for you and add two projects into it.  The first project is a web project where you'll implement your application.  The second is a testing project that you can use to write unit tests against it:

ASP.NET MVC Framework (Part 1)_第4张图片

You can use any unit testing framework (including NUnit, MBUnit, MSTest, XUnit, and others) with the ASP.NET MVC Framework.  VS 2008 Professional now includes built-in testing project support for MSTest (previously in VS 2005 this required a Visual Studio Team System SKU), and our default ASP.NET MVC project template automatically creates one of these projects when you use VS 2008. 

We'll also be shipping project template downloads for NUnit, MBUnit and other unit test frameworks as well, so if you prefer to use those instead you'll also have an easy one click way to create your application and have a test project immediately ready to use with it.

Understanding the Folder Structure of a Project

The default directory structure of an ASP.NET MVC Application has 3 top-level directories:

  • /Controllers
  • /Models
  • /Views

As you can probably guess, we recommend putting your Controller classes underneath the /Controllers directory, your data model classes underneath your /Models directory, and your view templates underneath your /Views directory. 

While the ASP.NET MVC framework doesn't force you to always use this structure, the default project templates use this pattern and we recommend it as an easy way to structure your application.  Unless you have a good reason to use an alternative file layout, I'd recommend using this default pattern.

Mapping URLs to Controller Classes

In most web frameworks (ASP, PHP, JSP, ASP.NET WebForms, etc), incoming URLs typically map to template files stored on disk.  For example, a "/Products.aspx" or "/Products.php" URL typically has an underlying Products.aspx or Products.php template file on disk that handles processing it.  When a http request for a web application comes into the web server, the web framework runs code specified by the template file on disk, and this code then owns handling the processing of the request.  Often this code uses the HTML markup within the Products.aspx or Products.php file to help with generating the response sent back to the client.

MVC frameworks typically map URLs to server code in a different way.  Instead of mapping URLs to template files on disk, they instead map URLs directly to classes.  These classes are called "Controllers" and they own processing incoming requests, handling user input and interactions, and executing appropriate application and data logic based on them.  A Controller class will then typically call a separate "View" component that owns generating the actual HTML output for the request.

ASP.NET MVC Framework (Part 1)_第5张图片

The ASP.NET MVC Framework includes a very powerful URL mapping engine that provides a lot of flexibility in how you map URLs to Controller classes.  You can use it to easily setup routing rules that ASP.NET will then use to evaluate incoming URLs and pick a Controller to execute.  You can also then have the routing engine automatically parse out variables that you define within the URL and have ASP.NET automatically pass these to your Controller as parameter arguments.  I'll be covering more advanced scenarios involving the URL routing engine in a future blog post in this series.

Default ASP.NET MVC URL Routing to Controller Classes

By default ASP.NET MVC projects have a preconfigured set of URL routing rules that enable you to easily get started on an application without needing to explicitly configure anything.  Instead you can start coding using a default set of name-based URL mapping conventions that are declared within the ASP.NET Application class of the Global.asax file created by the new ASP.NET MVC project template in Visual Studio. 

The default naming convention is to map the leading URL path of an incoming HTTP request (for example: /Products/) to a class whose name follows the pattern UrlPathController (for example: by default a URL leading with /Products/ would map to a class named ProductsController).

To build our e-commerce product browsing functionality, we'll add a new "ProductsController" class to our project (you can use the "Add New Item" menu in Visual Studio to easily create a Controller class from a template):

ASP.NET MVC Framework (Part 1)_第6张图片

Our ProductsController class will derive from the System.Web.MVC.Controller base class.  Deriving from this base class isn't required - but it contains some useful helper methods and functionality that we'll want to take advantage of later:

ASP.NET MVC Framework (Part 1)_第7张图片

Once we define this ProductsController class within our project, the ASP.NET MVC framework will by default use it to process all incoming application URLs that start under the "/Products/" URL namespace.  This means it will be automatically called to process the "/Products/Categories", "/Products/List/Beverages", and "/Products/Detail/3" URLs that we are going to want to enable within our store-front application.

In a future blog post we'll also add a ShoppingCartController (to enable end users to manage their shopping carts) and an AccountController (to enable end users to create new membership accounts on the site and login/logout of it).  Once we add these two new controller classes to our project, URLs that start with /ShoppingCart/ and /Account/ will automatically be routed to them for processing.

Note: The ASP.NET MVC framework does not require that you always use this naming convention pattern.  The only reason our application uses this by default is because there is a mapping rule that configures this that was automatically added to our ASP.NET Application Class when we created the new ASP.NET MVC Project using Visual Studio.  If you don't like this rule, or want to customize it to use a different URL mapping pattern, just go into the ASP.NET Application Class (in Global.asax) and change it. I'll cover how to-do this in a future blog post (when I'll also show some of the cool scenarios the URL routing engine enables).

Understanding Controller Action Methods

Now that we have a created a ProductsController class in our project we can start adding logic to handle the processing of incoming "/Products/" URLs to the application.

When defining our e-commerce storefront use cases at the beginning of this blog post, I said we were going to implement three scenarios on the site: 1) Browsing all of the Product Categories, 2) Listing Products within a specific Category, and 3) Showing Details about a Specific Product.  We are going to use the following SEO-friendly URLs to handle each of these scenarios:

URL Format Behavior URL Example
/Products/Categories Browse all Product Categories /Products/Categories
/Products/List/Category List Products within a Category /Products/List/Beverages
/Products/Detail/ProductID Show Details about a Specific Product /Products/Detail/34

There are a couple of ways we could write code within our ProductsController class to process these three types of incoming URLs.  One way would be to override the "Execute" method on the Controller base class and write our own manual if/else/switching logic to look at the incoming URL being requested and then execute the appropriate logic to process it.

A much easier approach, though, is to use a built-in feature of the MVC framework that enables us to define "action methods" on our controller, and then have the Controller base class automatically invoke the appropriate action method to execute based on the URL routing rules in use for our application.

For example, we could add the below three controller action methods to our ProductsController class to handle our three e-commerce URL scenarios above:

ASP.NET MVC Framework (Part 1)_第8张图片

The URL routing rules that are configured by default when a new project is created treat the URL sub-path that follows the controller name as the action name of the request.  So if we receive a URL request of /Products/Categories, the routing rule will treat "Categories" as the name of the action, and the Categories() method will be invoked to process the request.  If we receive a URL request of /Products/Detail/5, the routing rule will treat "Detail" as the name of the action, and the Detail() method will be invoked to process the request, etc. 

Note: The ASP.NET MVC framework does not require that you always use this action naming convention pattern.   If you want to use a different URL mapping pattern, just go into the ASP.NET Application Class (in Global.asax) and change it.

Mapping URL Parameters to Controller Action Methods

There are several ways to access URL parameter values within the action methods of Controller classes.

The Controller base class exposes a set of Request and Response objects that can be used.  These objects have the exact same API structure as the HttpRequest/HttpResponse objects that you are already familiar with in ASP.NET.  The one important difference is that these objects are now interface based instead of sealed classes (specifically: the MVC framework ships with new System.Web.IHttpRequest and System.Web.IHttpResponse interfaces).  The benefit of having these be interfaces is that it is now easy to mock them - which enables easy unit testing of controller classes.  I'll cover this in more depth in a future blog post. 

Below is an example of how we could use the Request API to manually retrieve an ID querystring value from within our Detail action method in the ProductsController class:

ASP.NET MVC Framework (Part 1)_第9张图片

The ASP.NET MVC framework also supports automatically mapping incoming URL parameter values as parameter arguments to action methods.  By default, if you have a parameter argument on your action method, the MVC framework will look at the incoming request data to see if there is a corresponding HTTP request value with the same name.  If there is, it will automatically pass it in as a parameter to your action method.

For example, we could re-write our Detail action method to take advantage of this support and make it cleaner like below:

ASP.NET MVC Framework (Part 1)_第10张图片

In addition to mapping argument values from the querystring/form collection of a request, the ASP.NET MVC framework also allows you to use the MVC URL route mapping infrastructure to embed parameter values within the core URL itself (for example: instead of /Products/Detail?id=3 you could instead use /Products/Detail/3). 

The default route mapping rule declared when you create a new MVC project is one with the format: "/[controller]/[action]/[id]".  What this means is that if there is any URL sub-path after the controller and action names in the URL, it will by default be treated as a parameter named "id" - and which can be automatically passed into our controller action method as a method argument.

This means that we can now use our Detail method to also handle taking the ID argument from the URL path (e.g: /Products/Detail/3):

ASP.NET MVC Framework (Part 1)_第11张图片

I can use a similar approach for the List action so that we can pass in the category name as part of the URL (for example: /Products/List/Beverages).  In the interest of making the code more readable, I made one tweak to the routing rules so that instead of having the argument name be called "id" it will be called "category" for this action.

Below is a version of our ProductsController class that now has full URL routing and parameter mapping support implemented:

ASP.NET MVC Framework (Part 1)_第12张图片

Note above how the List action takes the category parameter as part of the URL, and then an optional page index parameter as a querystring (we'll be implementing server-side paging and using that value to indicate which page of category data to display with the request). 

Optional parameters in our MVC framework are handled using nullable type arguments on Controller Action methods.  Because the page parameter on our List action is a nullable int (that is what "int?" means syntactically), the MVC framework will either pass in a value if it is present in the URL - or pass in null if not.  Check out my previous post on the ?? null coalescing operator to learn a useful tip/trick on how to work with nullable types that are passed as arguments like this.

Building our Data Model Objects

We now have a ProductsController class and three action methods on it ready to process incoming web requests.  Our next step will be to build some classes that will help us work with our database to retrieve the appropriate data needed to handle these web requests.

In an MVC world "models" are the components of an application that are responsible for maintaining state.  With web applications this state is typically persisted inside a database (for example: we might have a Product object that is used to represent product data from the Products table inside our SQL database).

The ASP.NET MVC Framework enables you to use any data access pattern or framework you want in order to retrieve and manage your models.  If you want to use ADO.NET DataSets/DataReaders (or abstractions built on top of them) you can.  If you prefer to use an object relational mapper (ORM) like NHibernate, LLBLGen, WilsonORMapper, LINQ to SQL/LINQ to Entities you can absolutely use those as well.

For our e-commerce sample application I'm going to use the built-in LINQ to SQL ORM shipped in .NET 3.5 and VS 2008.  You can learn more about LINQ to SQL from my ongoing blog tutorial series that covers it (in particular make sure to check out my Part1, Part2, Part3 and Part4 posts). 

I'll start by right-clicking on the "Models" sub-directory of our MVC web project inside VS and choose the "Add New Item" option to add a LINQ to SQL model.  Within the LINQ to SQL ORM designer I'll define three data model classes that map to the Categories, Products, and Suppliers table inside the SQL Server Northwind sample database (read Part 2 of my LINQ to SQL series to learn how to-do this):

ASP.NET MVC Framework (Part 1)_第13张图片

Once we've defined our LINQ to SQL data model classes, I'll then add a new NorthwindDataContext partial class to our Models directory as well:

ASP.NET MVC Framework (Part 1)_第14张图片

Within this class I'll define a few helper methods that encapsulate some LINQ expressions that we can use to retrieve the unique Category objects from our database, retrieve all Product objects within a specific category in our database, as well as retrieve an individual Product object based on a supplied ProductID:

ASP.NET MVC Framework (Part 1)_第15张图片

These helper methods will make it easy for us to cleanly retrieve the data model objects needed from our ProductsController class (without having to write the LINQ expressions within the Controller class itself):

ASP.NET MVC Framework (Part 1)_第16张图片

We now have all of the data code/objects we need to finish implementing our ProductsController functionality. 

Finishing the Implementation of our ProductsController Class

Controllers in a MVC based application are responsible for processing incoming requests, handling user input and interactions, and executing appropriate application logic based on them (retrieving and updating model data stored in a database, etc).

Controllers typically do not generate the specific HTML response for a request.  The task of generating an HTML response is instead owned by "View" components within the application - which are implemented as separate classes/templates from Controllers.  Views are intended to be focused entirely on encapsulating presentation logic, and should not contain any application logic or database retrieval code (instead all app logic should be handled by the Controller).

In a typical MVC web workflow, Controller action methods will handle the incoming web request, use the incoming parameter values to execute appropriate application logic code, retrieve or update data model objects from a database, and then select a "View" to use to render an appropriate UI response back to a browser.  As part of picking the appropriate View to render, the Controller will explicitly pass in (as arguments) all of the data and variables required by the "View" in order to for it to render the appropriate response:

ASP.NET MVC Framework (Part 1)_第17张图片

You might be wondering - what is the benefit of separating the Controller and the View like this?  Why not just put them in the same class?  The primary motivation in partitioning the application like this is to help enforce the separation of your application/data logic from your UI generation code.  This makes it much easier to unit test your application/data logic in isolation from your UI rendering logic.  It can also help make your application more maintainable over time - since it makes it harder for you to accidentally add application/data logic in your view templates.

When implementing the three controller action methods of our ProductsController class, we'll use the incoming URL parameter values to retrieve the appropriate model objects from our database, and then pick a "View" component to use to render an appropriate HTML response.  We'll use one of the RenderView() methods on the Controller base class to specify the View we want to use, as well as explicitly pass in the specific data that we want the View to use to render its response.

Below is the final result of our ProductsController implementation:

ASP.NET MVC Framework (Part 1)_第18张图片

Notice that the number of lines of code in our action methods above is pretty small (two lines each).  This is partly because the URL parameter parsing logic is handled entirely for us by the MVC framework (saving us from having to write a lot of this code).  This is also partly because the product browsing scenario is fairly simple from a business logic perspective (the action methods are all read-only display scenarios). 

In general, though, you'll often find that you'll have what are sometimes called "skinny controllers" - meaning controller methods full of relatively terse action methods (less than 10 lines of code).  This is often a good sign that you have cleanly encapsulated your data logic and factored your controller logic well.

Unit Testing our ProductsController

You might be surprised that the next step we are going to work on is to test our application logic and functionality.  You might ask - how is that even possible?  We haven't implemented our Views, and our application currently doesn't render a single tag of HTML.  Well, part of what makes an MVC approach attractive is that we can unit test the Controller and Model logic of applications completely independently of the View/Html generation logic.  As you'll see below we can even unit test these before we create our Views.

To unit test the ProductsController class that we've been working on, we'll add a ProductsControllerTest class into the Test Project that was added to our solution by default when we created our ASP.NET MVC Application using Visual Studio:

ASP.NET MVC Framework (Part 1)_第19张图片

We'll then define a simple unit test that tests the Detail action of our ProductsController:

ASP.NET MVC Framework (Part 1)_第20张图片

The ASP.NET MVC framework has been designed specifically to enable easy unit testing.  All core APIs and contracts within the framework are interfaces, and extensibility points are provided to enable easy injection and customization of objects (including the ability to use IOC containers like Windsor, StructureMap, Spring.NET, and ObjectBuilder).  Developers will be able to use built-in mock classes, or use any .NET type-mocking framework to simulate their own test versions of MVC related objects.

In the unit test above, you can see an example of how we are injecting a dummy "ViewFactory" implementation on our ProductsController class before calling the Detail() action method.  By doing this we are overriding the default ViewFactory that would otherwise handle creating and rendering our View.  We can use this test ViewFactory implementation to isolate the testing of just our ProductController's Detail action behavior (and not have to invoke the actual View to-do this).  Notice how we can then use the three Assert statements after the Detail() action method is called to verify that the correct behavior occurred within it (specifically that the action retrieved the correct Product object and then passed it to the appropriate View).

Because we can mock and simulate any object in the MVC framework (including IHttpRequest and IHttpResponse objects), you do not have to run unit tests in the context of an actual web-server.  Instead, we can create our ProductsController within a normal class library and test it directly.  This can significantly speed up the execution of unit tests, as well as simplify the configuration and running of them.

If we use the Visual Studio 2008 IDE, we can also easily track the success/failure of our test runs (this functionality is now built-into VS 2008 Professional):

ASP.NET MVC Framework (Part 1)_第21张图片

I think you'll find that the ASP.NET MVC Framework makes writing tests easy, and enables a nice TDD workflow.

Rendering UI with Views

We've finished implementing and testing the application + data logic for the product browsing section of our e-commerce application.  Now we need to implement the HTML UI for it. 

ASP.NET MVC Framework (Part 1)_第22张图片

We'll do this by implementing "Views" that render the appropriate UI using the view-related data objects that our ProductsController action method provided when calling the RenderView() method:

ASP.NET MVC Framework (Part 1)_第23张图片

In the code example above the RenderView method's "Categories" parameter is indicating the name of the view we want to render, and the second parameter is a list of category objects that we want to pass to the view and have it use as data to generate the appropriate HTML UI for.

The ASP.NET MVC Framework supports the ability to use any templating engine to help with generating UI (including existing templating engines like NVelocity, Brail - as well as new ones you write yourself).  By default the ASP.NET MVC Framework uses the existing ASP.NET Page (.aspx), Master Page (.master), and UserControl (.ascx) support already within ASP.NET. 

We'll be using the built-in ASP.NET view engine to implement our E-commerce Application UI.

Defining a Site.Master File

Because we are going to be building many pages for our site, we'll start our UI work by first defining a master page that we can use to encapsulate the common HTML layout/chrome across the site. We'll do this in a file called "Site.Master" that we'll create under the \Views\Shared directory of our project:

ASP.NET MVC Framework (Part 1)_第24张图片

We can reference an external CSS stylesheet to encapsulate all styles for the site, and then use the master page to define the overall layout of the site, as well as identify content placeholder regions where we'll want pages to be able to fill in page specific content.  We can optionally use all of the cool new VS 2008 designer features when doing this (including the HTML split-view designer, CSS Authoring, and Nested Master Pages support).

ASP.NET MVC Framework (Part 1)_第25张图片

Understanding the /Views Directory Structure

By default when you create a new ASP.NET MVC Project using Visual Studio, it will create a "Shared" sub-directory underneath the "Views" directory root.  This is the recommended place to store Master Pages, User Controls, and Views that we want to share across multiple Controllers within the application.

When building views that are specific to an individual controller, the default ASP.NET MVC convention is to store them in sub-directories under the \Views root.  By default the name of a sub-directory should correspond to the Controller name.  For example, because the Controller class we have been building is called "ProductsController", we will by default store the Views specific to it within the \Views\Products sub-directory:

ASP.NET MVC Framework (Part 1)_第26张图片

When we call the RenderView(string viewName) method within a specific Controller, the MVC framework will automatically first look for a corresponding .aspx or .ascx view template underneath the \Views\ControllerName directory, and then if it can't find an appropriate view template there it will check the \Views\Shared directory for one:

Creating a Categories View

We can create the "Categories" View for our ProductsController within Visual Studio by using the "Add New Item" menu option on the Products directory and selecting the "MVC View Page" item template.  This will create a new .aspx page that we can optionally associate with our Site.Master master page to pick up the overall look and feel of the site (and just like with master pages you get full WYSIWYG designer support):

ASP.NET MVC Framework (Part 1)_第27张图片

When building applications using an MVC pattern, you want to keep your View code as simple as possible, and make sure that the View code is purely about rendering UI.  Application and data retrieval logic should only be written inside Controller classes.  Controller classes can then choose to pass on the necessary data objects needed to render a view when they call their RenderView method.  For example, below in the Categories action method of our ProductsController class we are passing a List collection of Category objects to the Categories view:

ASP.NET MVC Framework (Part 1)_第28张图片

MVC View Pages by default derive from the System.Web.Mvc.ViewPage base class, which provides a number of MVC specific helper methods and properties that we can use in constructing our UI.  One of these ViewPage properties is named "ViewData", and it provides access to the view-specific data objects that the Controller passed as arguments to the RenderView() method.

From within your View you can access the "ViewData" in either a late-bound or strongly-typed way.  If your View derives from ViewPage, the ViewData property will be typed as a late-bound dictionary.  If your View derives from the generics based ViewPage<T> - where T indicates the data object type of the ViewData the Controller is passing to the View - then the ViewData property will be strongly typed to match the same type that your Controller passed in.

For example, my Categories View code-behind class below is deriving from ViewPage<T> - where I am indicating that T is a List of Category objects:

ASP.NET MVC Framework (Part 1)_第29张图片

This means that I get full type-safety, intellisense, and compile-time checking within my View code when working against the ProductsController.Categories() supplied List<Category> ViewData:

ASP.NET MVC Framework (Part 1)_第30张图片

Rendering our Categories View:

If you remember from the screenshots at the very beginning of this post, we want to display a list of the product categories within our Categories view:

ASP.NET MVC Framework (Part 1)_第31张图片

I can write this HTML UI generation code in one of two ways within my Categories View implementation: 1) Using Inline Code within the .aspx file, or 2) Using Server Controls within the .aspx file and Databinding from my Code Behind

Rendering Approach 1: Using Inline Code

ASP.NET Pages, User Controls and Master Pages today support <% %> and <%= %> syntax to embed rendering code within html markup.  We could use this technique within our Categories View to easily write a foreach loop that generates a bulleted HTML category list:

ASP.NET MVC Framework (Part 1)_第32张图片

VS 2008 provides full code intellisense within the source editor for both VB and C#.  This means we'll get intellisense against our Category model objects passed to the View:

ASP.NET MVC Framework (Part 1)_第33张图片

VS 2008 also provides full debugger support for inline code as well (allowing us to set breakpoints and dynamically inspect anything in the View with the debugger):

ASP.NET MVC Framework (Part 1)_第34张图片

Rendering Approach 2: Using Server Side Controls

 

ASP.NET Pages, User Controls and Master Pages also support the ability to use declarative server controls to encapsulate HTML UI generation.  Instead of using inline code like above, we could use the new <asp:listview> control in .NET 3.5 to generate our bulleted list UI instead:

ASP.NET MVC Framework (Part 1)_第35张图片

Notice above how the ListView control encapsulates both rendering a list of values, as well as handles the scenario where no items are in the list (the <EmptyDataTemplate> saves us from having to write an if/else statement in the markup).  We could then databind our list of category objects to the listview control using code-behind code like below:

ASP.NET MVC Framework (Part 1)_第36张图片

Important: In a MVC world we only want to put rendering logic in our View's code-behind class (and not any application or data logic).  Notice above how the only logic we have is to assign the strongly typed ViewData collection of Category objects to the ListView control.   Our ProductsController Controller class is the one that actually retrieves the list of Categories from the database - not the View. 

This ListView server-control version of our View template will then generate the exact same HTML as our in-line code version.  Because we don't have a <form runat="server"> control on the page, no viewstate, ID values or other markup will be emitted.  Only pure CSS friendly HTML:

ASP.NET MVC Framework (Part 1)_第37张图片 

Html.ActionLink Method

One of the things you might have noticed in both the inline-code and the server-control versions of the View code snippets above are calls to an Html.ActionLink method:

The Html object is a helper property on the ViewPage base class, and the ActionLink method is a helper on it that makes it easy to dynamically generate HTML hyperlinks that link back to action methods on Controllers. If you look at the HTML output picture in the section above, you can see some example HTML output generated by this method:

<a href="http://weblogs.asp.net/Products/List/Beverages">Beverages</a>

The signature of the Html.ActionLink helper method I am using looks like this:

string ActionLink(string text, object values);

The first argument represents the inner content of the hyperlink to render (for example: <a>text goes here</a>).  The second argument is an anonymous object that represents a sequence of values to use to help generate the actual URL (you can think of this as a cleaner way to generate dictionaries).  I will go into more detail on how exactly this works in a future blog post that covers the URL routing engine.  The short summary, though, is that you can use the URL routing system both to process incoming URLs, as well as to generate URLs that you can emit in outgoing HTML.  If we have a routing rule like this:

/<controller>/<action>/<category>

And then write this code within a ProductController's Category View:

<%= Html.ActionLink("Click Me to See Beverages", new { action="List", category="Beverages" } %>

The ActionLink method will use the URL mapping rules of your application to swap in your parameters and generate this output:

<a href="http://weblogs.asp.net/Products/List/Beverages">Click Me to See Beverages</a>

This makes it easy within your application to generate URLs and AJAX callbacks to your Controllers.  It also means you can update your URL routing rules in one place and have the code throughout your application automatically pick up the changes for both incoming URL processing and outgoing URL generation.

Important Note: To help enforce testability, the MVC framework today does not support postback events directly to server controls within your Views.  Instead, ASP.NET MVC applications generate hyperlink and AJAX callbacks to Controller actions - and then use Views (and any server controls within them) solely to render output.  This helps ensure that your View logic stays minimal and solely focused on rendering, and that you can easily unit test your Controller classes and verify all Application and Data Logic behavior independent of your Views.  I'll blog more about this in the future.

Summary

This first blog post is a pretty long one, but hopefully helps provide a reasonably broad look at how all the different components of the new ASP.NET MVC Framework fit together, and how you can build a common real world scenario with it.  The first public preview of the ASP.NET MVC bits will be available in a few weeks, and you'll be able to use them to do all of the steps I outlined above.

While many of the concepts inherent to MVC (in particular the idea of separation of concerns) are probably new to a lot of people reading this, hopefully this blog post has also show how the ASP.NET MVC implementation we've been working on fits pretty cleanly into the existing ASP.NET, .NET, and Visual Studio feature-set.  You can use .ASPX, .ASCX and .MASTER files and ASP.NET AJAX to create your ASP.NET MVC Views.  Non-UI features in ASP.NET today like Forms Authentication, Windows Authentication, Membership, Roles, Url Authorization, Caching, Session State, Profiles, Health Monitoring, Configuration, Compilation, Localization, and HttpModules/HttpHandlers all fully support the MVC model.

If you don't like the MVC model or don't find it natural to your style of development, you definitely don't have to use it.  It is a totally optional offering - and does not replace the existing WebForms Page Controller model.  Both WebForms and MVC will be fully supported and enhanced going forward.  You can even build a single application and have parts of it written using WebForms and parts written using an MVC approach if you want.

If you do like what you've seen from the above MVC post (or are intrigued and want to learn more), keep an eye on my blog over the weeks ahead.  I'll be covering more MVC concepts and use them to build out our e-commerce application to show more features of it.

Hope this helps,

Scott

你可能感兴趣的:(framework)