Maarten Balliauw {blog}

ASP.NET MVC, Microsoft Azure, PHP, web development ...

NAVIGATION - SEARCH

Introduction to ASP.NET MVC for VISUG - Presentation materials

VISUG Yesterday evening, I did a presentation on the ASP.NET MVC framework for VISUG (Visual Studio User Group Belgium). I really hope everyone got a good feel on what the ASP.NET MVC framework is all about and what it takes to build an ASP.NET MVC application. Thank you Pieter Gheysens for inviting me for this talk! And thank you audience for being interested for over an hour and a half!

A recorded version of this presentation will be available later, for the moment you'll have to do with the presentation materials. The download contains the slides, the Hello World application and the testing demo. The CarTrackr application can be found on CodePlex.

Downloads

Presentation materials: VISUG ASP.NET MVC materials.zip (5.63 mb)
CarTrackr sample application: http://www.codeplex.com/CarTrackr/

kick it on DotNetKicks.com

Using the ASP.NET MVC ModelBinder attribute - Second part

Just after the ASP.NET MVC preview 5 was released, I made a quick attempt to using the ModelBinder attribute. In short, a ModelBinder allows you to use complex objects as action method parameters, instead of just basic types like strings and integers. While my aproach was correct, it did not really cover the whole picture. So here it is: the full picture.

First of all, what are these model binders all about? By default, an action method would look like this:

[code:c#]

public ActionResult Edit(int personId) {
    // ... fetch Person and do stuff
}

[/code]

Now wouldn't it be nice to pass this Person object completely as a parameter, rather than obliging the controller's action method to process an id? Think of this:

[code:c#]

public ActionResult Edit(Person person) {
    // ... do stuff
}

[/code]

Some advantages I see:

  • More testable code!
  • Easy to work with!
  • Some sort of viewstate-thing which just passes a complete object back and forth. Yes, I know, ViewState is BAD! But I recently had a question about how to manage concurrency, and using a version id as an hidden HTML field or a complete object as a hidden HTML field should not be that bad, no?

Just one thing to do: implementing a ModelBinder which converts HTML serialized Persons into real Persons (well, objects, not "real" real persons...)

How to implement it...

Utility functions

No comments, just two utility functions which serialize and deserialize an object to a string an vice-versa.

[code:c#]

using System;
using System.IO;
using System.Runtime.Serialization.Formatters.Binary;

namespace ModelBinderDemo.Util
{
    public static class Serializer
    {
        public static string Serialize(object subject)
        {
            MemoryStream ms = new MemoryStream();
            BinaryFormatter bf = new BinaryFormatter();
            bf.Serialize(ms, subject);

            return Convert.ToBase64String(ms.ToArray());
        }

        public static object Deserialize(string subject)
        {
            MemoryStream ms = new MemoryStream(Convert.FromBase64String(subject));
            BinaryFormatter bf = new BinaryFormatter();
            return bf.Deserialize(ms);
        }
    }
}

[/code]

Creating a ModelBinder

The ModelBinder itself should be quite simple to do. Just create a class which inherits DefaultModelBinder and have it ocnvert a string into an object. Beware! The passed in value might also be an array of strings, so make sure to verify that in code.

[code:c#]

using System;
using System.Globalization;
using System.Web.Mvc;
using ModelBinderDemo.Models;
using ModelBinderDemo.Util;

namespace ModelBinderDemo.Binders
{
    public class PersonBinder : DefaultModelBinder
    {
        protected override object ConvertType(CultureInfo culture, object value, Type destinationType)
        {
            // Only accept Person objects for conversion
            if (destinationType != typeof(Person))
            {
                return base.ConvertType(culture, value, destinationType);
            }

            // Get the serialized Person that is being passed in.
            string serializedPerson = value as string;
            if (serializedPerson == null && value is string[])
            {
                serializedPerson = ((string[])value)[0];
            }

            // Convert to Person
            return Serializer.Deserialize(serializedPerson);
        }
    }
}

[/code]

In order to use this ModelBinder, you'll have to register it in Global.asax.cs:

[code:c#]

// Register model binders
ModelBinders.Binders.Add(typeof(Person), new PersonBinder());

[/code]

Great View!

Nothing strange in the View: just a HTML form which generates a table to edit a Person's details and a submit button.

[code:c#]

<%@ Page Language="C#" MasterPageFile="~/Views/Shared/Site.Master" AutoEventWireup="true" CodeBehind="Index.aspx.cs" Inherits="ModelBinderDemo.Views.Home.Index" %>
<%@ Import Namespace="ModelBinderDemo.Models" %>

<asp:Content ID="indexContent" ContentPlaceHolderID="MainContent" runat="server">
    <h2>Edit person</h2>
    <% using (Html.Form("Home", "Index", FormMethod.Post)) { %>
        <%=Html.Hidden("person", ViewData["Person"]) %>
        <%=Html.ValidationSummary()%>
        <table border="0" cellpadding="2" cellspacing="0">
            <tr>
                <td>Name:</td>
                <td><%=Html.TextBox("Name", ViewData.Model.Name)%></td>
            </tr>
            <tr>
                <td>E-mail:</td>
                <td><%=Html.TextBox("Email", ViewData.Model.Email)%></td>
            </tr>
            <tr>
                <td>&nbsp;</td>
                <td><%=Html.SubmitButton("saveButton", "Save")%></td>
            </tr>
        </table>
    <% } %>
</asp:Content>

[/code]

Wait! One thing to notice here! The <%=Html.Hidden("person", ViewData["Person"]) %> actually renders a hidden HTML field, containing a serialized version of my Person. Which might look like this:

[code:c#]

<input Length="340" id="person" name="person" type="hidden" value="AAEAAAD/////AQAAAAAAAAAMAgAAAEZNb2RlbEJpbmRlckRlbW8
sIFZlcnNpb249MS4wLjAuMCwgQ3VsdHVyZT1uZXV0cmFsLCBQdWJsaWNLZXlUb2tlbj1udWxsBQEAAAAdTW9kZWxCaW5kZXJEZW1vLk1v
ZGVscy5QZXJzb24DAAAAEzxJZD5rX19CYWNraW5nRmllbGQVPE5hbWU+a19fQmFja2luZ0ZpZWxkFjxFbWFpbD5rX19CYWNraW5nRmllb
GQAAQEIAgAAAAEAAAAGAwAAAAdNYWFydGVuBgQAAAAabWFhcnRlbkBtYWFydGVuYmFsbGlhdXcuYmUL" />

[/code]

Creating the action method

All preparations are done, it's time for some action (method)! Just accept a HTTP POST, accept a Person object in a variable named person, and Bob's your uncle! The person variable will contain a real Person instance, which has been converted from AAEAAD.... into a real instance. Thank you, ModelBinder!

[code:c#]

[AcceptVerbs("POST")]
public ActionResult Index(Person person, FormCollection form)
{
    if (string.IsNullOrEmpty(person.Name))
    {
        ViewData.ModelState.AddModelError("Name", person.Name, "Plese enter a name.");
    }

    if (string.IsNullOrEmpty(person.Email))
    {
        ViewData.ModelState.AddModelError("Name", person.Name, "Plese enter a name.");
    }

    return View("Index", person);
}

[/code]

Make sure to download the full source and see it in action! ModelBinderDemo2.zip (239.87 kb)

kick it on DotNetKicks.com

ASP.NET MVC preview 5's AntiForgeryToken helper method and attribute

The new ASP.NET MVC preview 5 featured a number of new HtmlHelper methods. One of these methods is the HtmlHelper.AntiForgeryToken. When you place <%=Html.AntiForgeryToken()%> on your view, this will be rendered similar to the following:

[code:c#]

<input name="__MVC_AntiForgeryToken" type="hidden" value="Ak8uFC1MQcl2DXfJyOM4DDL0zvqc93fTJd+tYxaBN6aIGvwOzL8MA6TDWTj1rRTq" />

[/code]

When using this in conjunction with the action filter attribute [ValidateAntiForgeryToken], each round trip to the server will be validated based on this token.

[code:c#]

[ValidateAntiForgeryToken]
public ActionResult Update(int? id, string name, string email) {
    // ...
}

[/code]

Whenever someone tampers with this hidden HTML field's data or posts to the action method from another rendered view instance, this ValidateAntiForgeryToken will throw a AntiForgeryTokenValidationException.

kick it on DotNetKicks.com

Using the ASP.NET MVC ModelBinder attribute

ASP.NET MVC action methods can be developed using regular method parameters. In earlier versions of the ASP.NET MVC framework, these parameters were all simple types like integers, strings, booleans, … When required, a method parameter can be a complex type like a Contact with Name, Email and Message properties. It is, however, required to add a ModelBinder attribute in this case.

Here’s how a controller action method could look like:

[code:c#]

public ActionResult Contact([ModelBinder(typeof(ContactBinder))]Contact contact)
{
    // Add data to view
    ViewData["name"] = contact.Name;
    ViewData["email"] = contact.Email;
    ViewData["message"] = contact.Message;
    ViewData["title"] = "Succes!";

    // Done!
    return View();
}

[/code]

Notice the ModelBinder attribute on the action method’s contact parameter. It also references the ContactBinder type, which is an implementation of IModelBinder that also has to be created in order to allow complex parameters:

[code:c#]

public class ContactBinder : IModelBinder
{
    #region IModelBinder Members

    public object GetValue(ControllerContext controllerContext, string modelName, Type modelType, ModelStateDictionary modelState)
    {
        if (modelType == typeof(Contact))
        {
            return new Contact
            {
                Name = controllerContext.HttpContext.Request.Form["name"] ?? "",
                Email = controllerContext.HttpContext.Request.Form["email"] ?? "",
                Message = controllerContext.HttpContext.Request.Form["message"] ?? ""
            };
        }

        return null;
    }

    #endregion
}

[/code]

UPDATE: Also check Timothy's blog post on this one.
UPDATE: And my follow-up blog post.

kick it on DotNetKicks.com

Form validation with ASP.NET MVC preview 5

In earlier ASP.NET MVC previews, form validation was something that should be implemented "by hand". Since the new ASP.NET MVC preview 5, form validation has become more handy. Let me show you how you can add validation in such a ridiculously easy manner.

Here's an example controller:

[code:c#]

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using System.Web.Mvc.Ajax;

namespace ValidationExample.Controllers
{
    [HandleError]
    public class HomeController : Controller
    {
        // ... some other action methods ...

        [AcceptVerbs("GET")]
        public ActionResult Contact()
        {
            return View();
        }

        [AcceptVerbs("POST")]
        public ActionResult Contact(string name, string email, string message)
        {
            // Add data to view
            ViewData["name"] = name;
            ViewData["email"] = email;
            ViewData["message"] = message;

            // Validation
            if (string.IsNullOrEmpty(name))
                ViewData.ModelState.AddModelError("name", name, "Please enter your name!");
            if (string.IsNullOrEmpty(email))
                ViewData.ModelState.AddModelError("email", email, "Please enter your e-mail!");
            if (!string.IsNullOrEmpty(email) && !email.Contains("@"))
                ViewData.ModelState.AddModelError("email", email, "Please enter a valid e-mail!");
            if (string.IsNullOrEmpty(message))
                ViewData.ModelState.AddModelError("message", message, "Please enter a message!");

            // Send e-mail?
            if (ViewData.ModelState.IsValid)
            {
                // send email...
                return RedirectToAction("Index");
            }
            else
            {
                return View();
            }
        }
    }
}

[/code]

You may notice an starnge thing here... Why is Contact defined twice, and why is it with this strange AcceptVerbs attribute? The AcceptVerbs attribute determines which action method to call, based on the HTTP method of the request. In this case, when I do not post a form, the first action method will be called, simply rendering a view. When posting a form, the second action method will be called, allowing me to do some validations.

Speaking of validations... Notice that I can set errors on the ViewData.ModelState collection, and use this ViewData.ModelState.IsValid property to check if everything is OK.

UPDATE: You can also use the controller's UpdateModel method (which updates a model object with form values) for setting data on the model. If the model throws an exception, this will be added to the ViewData.ModelState dictionary too.

One thing left with validation: the view itself!

[code:html]

<%@ Page Title="" Language="C#" MasterPageFile="~/Views/Shared/Site.Master" AutoEventWireup="true" CodeBehind="Contact.aspx.cs" Inherits="ValidationExample.Views.Home.Contact" %>

<asp:Content ID="Content1" ContentPlaceHolderID="MainContent" runat="server">
    <h2>Contact Us</h2>
    <p><%=Html.ValidationSummary()%></p>
    <% using (Html.Form<ValidationExample.Controllers.HomeController>( c => c.Contact("", "", ""), FormMethod.Post)) { %>
        <table border="0" cellpadding="2" cellspacing="0">
            <tr>
                <td>Name:</td>
                <td>
                    <%=Html.TextBox("name", ViewData["name"] ?? "")%>
                    <%=Html.ValidationMessage("name")%>
                </td>
            </tr>
            <tr>
                <td>Email:</td>
                <td>
                    <%=Html.TextBox("email", ViewData["email"] ?? "")%>
                    <%=Html.ValidationMessage("email")%>
                </td>
            </tr>
            <tr>
                <td colspan="2">Message:</td>
            </tr>
            <tr>
                <td colspan="2">
                    <%=Html.TextArea("message", ViewData["message"] ?? "")%>
                </td>
            </tr>
            <tr>
                <td colspan="2">
                    <%=Html.ValidationMessage("message")%>
                </td>
            </tr>
            <tr>
                <td>&nbsp;</td>
                <td>
                    <%=Html.SubmitButton("send", "Send e-mail")%>
                </td>
            </tr>
        </table>
    <% } %>
</asp:Content>

[/code]

Notice that there are 2 new HtmlHelper extension methods: ValidationMessage and ValidationSummary. The first one displays a validation message for one key in the ViewData.ModelState collection, while the latter displays a validation summary of all messages. Here's what my invalid post looks like:

Validation example

kick it on DotNetKicks.com

Building an ASP.NET MVC sitemap provider with security trimming

Warning!
Warning!
A new version of the source code provided in this post is available here. Use this blog post as reference only. 


Yes, it has been a while since my last post. A nice vacation to Austria, some work to catch up, ... All excuses, I know, but I'll make it up to you with a huge blog post!

If you have been using the ASP.NET MVC framework, you possibly have been searching for something like the classic ASP.NET sitemap. After you've played with it, you even found it useful! But not really flexible and easy to map to routes and controllers. Sounds familiar? Continue reading! Doesn't ring a bell? Well, continue reading, please!

Feel free to download the sample code.
UPDATE: A version for preview 5 can also be downloaded: MvcSitemapProvider.cs (19.46 kb)

The base concept of this class is based on someone else's version which supports dynamic nodes, populated by code instead of XML. Unfortunately, I forgot to write down the URL where I found it. So please, if you do find something like that, let me know so I can thank this person for the base concepts of his class...

kick it on DotNetKicks.com

The concept

What I would like, is having a web.sitemap file which looks like the following:

[code:c#]

<?xml version="1.0" encoding="utf-8" ?>
<siteMap>
  <siteMapNode id="Root" url="~/Index.aspx">
    <mvcSiteMapNode id="Home" title="Home" controller="Home" action="Index">
      <mvcSiteMapNode id="About" title="About Us" controller="Home" action="About" />
    </mvcSiteMapNode>

    <mvcSiteMapNode id="Products" title="Products" controller="Products">
      <mvcSiteMapNode id="Books" title="Books" controller="Products" action="List" category="Books" />
      <mvcSiteMapNode id="DVD" title="DVD's" controller="Products" action="List" category="DVD"/>
    </mvcSiteMapNode>
    <mvcSiteMapNode id="Account" title="Account" controller="Account">
      <mvcSiteMapNode id="Login" title="Login" controller="Account" action="Login" />
      <mvcSiteMapNode id="Register" title="Account Creation" controller="Account" action="Register" />
      <mvcSiteMapNode id="ChangePassword" title="Change Password" controller="Account" action="ChangePassword" />
      <mvcSiteMapNode id="Logout" title="Logout" controller="Account" action="Logout" />
    </mvcSiteMapNode>
  </siteMapNode>
</siteMap>

[/code]

That's right: regular siteMapNodes, but also mvcSiteMapNodes! I want my ASP.NET menu control and sitemap path to use both node types for determining the current locattion on my website. And since the ASP.NET MVC framework uses routing and allows extra parameters to build up a URL, I thought of creating an mvcSiteMapNode.

Each mvcSiteMapNode is structured like this:

id Id for the current node. Can only occur once!
title The title to show in menu's.
description Optional description.
controller The controller to map this node to. Will default to "Home" if it is not specified.
action The action on that controller to map this node to. Will default to "Index" if it is not specified.
* Well, any other attribute will be used as route data values. For example, if you add "category='Books'", it will correpond with new { category = "Books" } in your route definitions.
paramid Well, this one maps to new { id = ... }, since I already used id before...

Implementing it

Two options for this one... Option one would be extending the existing XmlSiteMapProvider class, but that seemed like a no-go because... well... I wanted to take the hard way :-) Option two it is! And that's extending StaticSiteMapProvider.

This MvcSiteMapProvider class will have to do some things:

  • Read the web.config settings
  • Cache my sitemap nodes for a specified amount of time
  • Do some mapping of the current HttpContext (which is not IHttpContext, unfortunately...) to the current route
  • Security trimming! The provider should check my controllers for AuthorizeAttribute and follow the directions of that attribute.

If you want to check the full source code, feel free to download it. I'll not go trough it completely in this blog post, but just pick some interesting parts.

MvcSiteMapNode

First things first! If I want to use a custom sitemap node, I'll have to create one! Here's my overloaded version of the SiteMapNode class which now also contains a Controller and Action property:

[code:c#]

/// <summary>
/// MvcSiteMapNode
/// </summary>
public class MvcSiteMapNode : SiteMapNode
{

    #region Properties

    public string Id { get; set; }
    public string Controller { get; set; }
    public string Action { get; set; }

    #endregion

    #region Constructor

    /// <summary>
    /// Creates a new MvcSiteMapNode instance
    /// </summary>
    public MvcSiteMapNode(SiteMapProvider provider, string key)
        : base(provider, key)
    {
        Id = key;
    }

    #endregion

}

[/code]

Reading the mvcSiteMapNode XML

That's actually a nice one! Here's the full snippet:

[code:c#]

/// <summary>
/// Maps an XMLElement from the XML file to a SiteMapNode.
/// </summary>
/// <param name="node">The element to map.</param>
/// <returns>A SiteMapNode which represents the XMLElement.</returns>
protected SiteMapNode GetMvcSiteMapNodeFromXMLElement(XElement node)
{
    // Get the ID attribute, need this so we can get the key.
    string id = GetAttributeValue(node.Attribute("id"));

    // Create a new sitemapnode, setting the key and url
    var smNode = new MvcSiteMapNode(this, id);

    // Create a route data dictionary
    IDictionary<string, object> routeValues = new Dictionary<string, object>();

    // Add each attribute to our attributes collection on the sitemapnode
    // and to a route data dictionary.
    foreach (XAttribute attribute in node.Attributes())
    {
        string attributeName = attribute.Name.ToString();
        string attributeValue = attribute.Value;

        smNode[attributeName] = attributeValue;

        if (attributeName != "title" && attributeName != "description"
            && attributeName != "resourceKey" && attributeName != "id"
            && attributeName != "paramid")
        {
            routeValues.Add(attributeName, attributeValue);
        }
        else if (attributeName == "paramid")

        {
            routeValues.Add("id", attributeValue);
        }
    }

    // Set the other properties on the sitemapnode,
    // these are for title and description, these come
    // from the nodes attrbutes are we populated all attributes
    // from the xml to the node.
    smNode.Title = smNode["title"];
    smNode.Description = smNode["description"];
    smNode.ResourceKey = smNode["resourceKey"];
    smNode.Controller = smNode["controller"];
    smNode.Action = smNode["action"] ?? "Index";

    // Verify route values
    if (!routeValues.ContainsKey("controller")) routeValues.Add("controller", "Home");
    if (!routeValues.ContainsKey("action")) routeValues.Add("action", "Index");

    // Build URL
    MvcHandler handler = HttpContext.Current.Handler as MvcHandler;
    RouteData routeData = handler.RequestContext.RouteData;

    smNode.Url = "~/" + routeData.Route.GetVirtualPath(handler.RequestContext, new RouteValueDictionary(routeValues)).VirtualPath;

    return smNode;
}

[/code]

Interesting part to note are the last 4 lines of code. I'm using the application's route data to map controller, action and values to a virtual path, which will be used by all sitemap controls to link to a URL. Coolness! If I change my routes in Global.asax.cs, my menu will automatically be updated without having to change my web.sitemap file.

Security trimming

Some more code. I told you it would be a long post!

[code:c#]

/// <summary>
/// Determine if a node is accessible for a user
/// </summary>
/// <param name="context">Current HttpContext</param>
/// <param name="node">Sitemap node</param>
/// <returns>True/false if the node is accessible</returns>
public override bool IsAccessibleToUser(HttpContext context, SiteMapNode node)
{
    // Is security trimming enabled?
    if (!this.SecurityTrimmingEnabled)
        return true;

    // Is it a regular node? No need for more things to do!
    MvcSiteMapNode mvcNode = node as MvcSiteMapNode;
    if (mvcNode == null)
        return base.IsAccessibleToUser(context, node);

    // Find current handler
    MvcHandler handler = context.Handler as MvcHandler;

    if (handler != null)
    {
        // It's an MvcSiteMapNode, try to figure out the controller class
        IController controller = ControllerBuilder.Current.GetControllerFactory().CreateController(handler.RequestContext, mvcNode.Controller);

        // Find all AuthorizeAttributes on the controller class and action method
        object[] controllerAttributes = controller.GetType().GetCustomAttributes(typeof(AuthorizeAttribute), true);
        object[] actionAttributes = controller.GetType().GetMethod(mvcNode.Action).GetCustomAttributes(typeof(AuthorizeAttribute), true);

        // Attributes found?
        if (controllerAttributes.Length == 0 && actionAttributes.Length == 0)
            return true;

        // Find out current principal
        IPrincipal principal = handler.RequestContext.HttpContext.User;

        // Find out configuration
        string roles = "";
        string users = "";
        if (controllerAttributes.Length > 0)
        {
            AuthorizeAttribute attribute = controllerAttributes[0] as AuthorizeAttribute;
            roles += attribute.Roles;
            users += attribute.Users;
        }
        if (actionAttributes.Length > 0)
        {
            AuthorizeAttribute attribute = actionAttributes[0] as AuthorizeAttribute;
            roles += attribute.Roles;
            users += attribute.Users;
         }

        // Still need security trimming?
        if (string.IsNullOrEmpty(roles) && string.IsNullOrEmpty(users) && principal.Identity.IsAuthenticated)
            return true;

            // Determine if the current user is allowed to access the current node
            string[] roleArray = roles.Split(',');
            string[] usersArray = users.Split(',');
            foreach (string role in roleArray)
            {
                if (role != "*" && !principal.IsInRole(role)) return false;
            }
            foreach (string user in usersArray)
            {
                if (user != "*" && (principal.Identity.Name == "" || principal.Identity.Name != user)) return false;
            }

            return true;
    }

    return false;
}

[/code]

Now read it again, it might be a bit confusing. What actually happens, is the following:

  1. Security trimming is not enabled? Well duh! Of cource you can access this node!
  2. If the curent node that a menu control or something similar tries to render is a regular sitemap node, simply use the base class to verify security
  3. If it is an MvcSiteMapNode that we're accessing, do some work...
    1. Find out the controller and action method that's being called
    2. Check for security attributes on the controller
    3. Check for security attributes on the action method
    4. Verify if the current IPrincipal complies with all previous stuff
  4. No access granted in the past few lines of code? return false!

I can now actually hide a sitemap node from unauthorized users by simply adding the [Authorize(...)] attribute to a controller action!

Using it

Feel free to download the sample code or check the live demo. It has been configured to use my custom sitemap provider by adding the following in web.config:

[code:xml]

<system.web>
    <!-- ... -->
    <siteMap defaultProvider="MvcSitemapProvider">
        <providers>
            <add name="MvcSitemapProvider"
                 type="MvcSitemapProviderDemo.Core.MvcSitemapProvider"
                 siteMapFile="~/Web.sitemap" securityTrimmingEnabled="true"
                 cacheDuration="10" />
        </providers>
    </siteMap>
    <!-- ... -->
</system.web>

[/code]

In short: I've told ASP.NET to use my sitemap provider in favor of the standard sitemap provider. Don't you just love this provider model!

Known issues

  • The root node should always link to url "~/Index.aspx"
  • A controller + action + values combination can only occur once (but that's the case with regular sitemaps too)

Note: based on ASP.NET MVC preview 4 - A version for preview 5 can also be downloaded: MvcSiteMapProvider.cs (19.90 kb)

kick it on DotNetKicks.com 

MSDN Chopsticks on ASP.NET MVC (screencasts)

A while ago, KatrienDG asked me to do some screencasts on the ASP.NET MVC framework for the MSDN Chopsticks page. I've been working on 2 screencasts: an introductory talk to the ASP.NET MVC framework and a Test Driven Development story. Feel free to leave some comments!

kick it on DotNetKicks.com

Introduction to ASP.NET's MVC framework

Abstract: "The ASP.NET MVC framework is a new approach to web development, based on the model-view-controller design pattern. Microsoft built this framework on top of ASP.NET to allow this alternative to work with existing features like membership caching, user controls... In this video, Maarten shows you some basics on the ASP.NET MVC framework like creating a new controller action and a view."

Test Driven Development with the ASP.NET MVC framework

Abstract: "This video explains you how to develop ASP.NET MVC web applications using 2 different approaches: regular development and test-driven development."

Example code: MvcTodoList.zip (503.21 kb)

ASP.NET MVC - Upcoming preview 4 release

ScottGu just posted that there's an upcoming preview 4 release of the ASP.NET MVC framework. What I immediately noticed, is that there are actually some community concepts being integrated in the framework, yay! And what's even cooler: 2 of these new features are things that I've already contributed to the community (the fact that it these are included in the MVC framework now could be coincidence, though...).

Thank you, ASP.NET MVC team! This preview 4 release seems like a great step in the evolution of the ASP.NET MVC framework. Thumbs up!

kick it on DotNetKicks.com

Article on ASP.NET MVC in .NET magazine #21

.NET magazine 21 Yesterday, I received the new Dutch edition of .NET magazine containing my article on the ASP.NET MVC framework. Since the article was written quite a while ago, soucre code is no longer up-to-date. Readers who are interested (or anyone else interested in ASP.NET MVC) can download up-to-date code examples on the ASP.NET MVC guestbook page.

kick it on DotNetKicks.com