Maarten Balliauw {blog}

Web development, NuGet, Microsoft Azure, PHP, ...

NAVIGATION - SEARCH

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

Extending ASP.NET MVC OutputCache ActionFilterAttribute - Adding substitution

In my previous blog post on ASP.NET MVC OutputCache, not all aspects of "classic" ASP.NET output caching were covered. For instance, substitution of cached pages. Allow me to explain...

When using output caching you might want to have everything cached, except, for example, a user's login name or a time stamp. When caching a full HTTP response, it is not really possible to inject dynamic data. ASP.NET introduced the Substitution control, which allows parts of a cached response to be dynamic. The contents of the Substitution control are dynamically injected after retrieving cached data, by calling a certain static method which returns string data. Now let's build this into my OutputCache ActionFilterAttribute...

UPDATE: Also check Phil Haack's approach to this: http://haacked.com/archive/2008/11/05/donut-caching-in-asp.net-mvc.aspx

1. But... how?

Schematically, the substitution process would look like this:

ASP.NET MVC OutputCache

When te view is rendered, it outputs a special substitution tag (well, special... just a HTML comment which will be recognized by the OutputCache). The OutputCache will look for these substitution tags and call the relevant methods to provide contents. A substitution tag will look like <!--SUBSTITUTION:CLASSNAME:METHODNAME-->.

One side note: this will only work with server-side caching (duh!). Client-side could also be realized, but that would involve some Ajax calls.

2. Creating a HtmlHelper extension method

Every developer loves easy-to-use syntax, so instead of writing an error-prone HTML comment like <!--SUBSTITUTION:CLASSNAME:METHODNAME-->. myself, let's do that using an extension method which allows syntax like <%=Html.Substitution<MvcCaching.Views.Home.Index>("SubstituteDate")%>. Here's an example view:

[code:c#]

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

<asp:Content ID="indexContent" ContentPlaceHolderID="MainContent" runat="server">
    <h2><%= Html.Encode(ViewData["Message"]) %></h2>

    <p>
        Cached timestamp: <%=Html.Encode(DateTime.Now.ToString())%>
    </p>

    <p>
        Uncached timestamp (substitution):
        <%=Html.Substitution<MvcCaching.Views.Home.Index>("SubstituteDate")%>
    </p>
</asp:Content>

[/code]

The extension method for this will look quite easy. Create a new static class containing this static method:

[code:c#]

public static class CacheExtensions
{
    public static string Substitution<T>(this HtmlHelper helper, string method)
    {
        // Check input
        if (typeof(T).GetMethod(method, BindingFlags.Static | BindingFlags.Public) == null)
        {
            throw new ArgumentException(
                string.Format("Type {0} does not implement a static method named {1}.",
                    typeof(T).FullName, method),
                        "method");
        }

        // Write output
        StringBuilder sb = new StringBuilder();

        sb.Append("<!--");
        sb.Append("SUBSTITUTION:");
        sb.Append(typeof(T).FullName);
        sb.Append(":");
        sb.Append(method);
        sb.Append("-->");

        return sb.ToString();
    }
}

[/code]

What happens is basically checking for the existance of the specified class and method, and rendering the appropriate HTML comment. Our example above will output <!--SUBSTITUTION:MvcCaching.Views.Home.Index:SubstituteDate-->.

One thing to do before substituting data though: defining the SubstituteDate method on the MvcCaching.Views.Home.Index: view codebehind. The signature of this method should be static, returning a string and accepting a ControllerContext parameter. In developer language: static string MyMethod(ControllerContext context);

Here's an example:

[code:c#]

public partial class Index : ViewPage
{
    public static string SubstituteDate(ControllerContext context)
    {
        return DateTime.Now.ToString();
    }
}

[/code]

3. Extending the OutputCache ActionFilterAttribute

Previously, we did server-side output caching by implementing 2 overrides of the ActionFilterAttribute, namely OnResultExecuting and OnResultExecuted. To provide substitution support, we'll have to modify these 2 overloads a little. Basically, just pass all output through the ResolveSubstitutions method. Here's the updated OnResultExecuted overload:

[code:c#]

public override void OnResultExecuted(ResultExecutedContext filterContext)
{
    // Server-side caching?
    if (CachePolicy == CachePolicy.Server || CachePolicy == CachePolicy.ClientAndServer)
    {
        if (!cacheHit)
        {
            // Fetch output
            string output = writer.ToString();

            // Restore the old context
            System.Web.HttpContext.Current = existingContext;

            // Fix substitutions
            output = ResolveSubstitutions(filterContext, output);

            // Return rendered data
            existingContext.Response.Write(output);

            // Add data to cache
            cache.Add(
                GenerateKey(filterContext),
                writer.ToString(),
                null,
                DateTime.Now.AddSeconds(Duration),
                Cache.NoSlidingExpiration,
                CacheItemPriority.Normal,
                null);
        }
    }
}

[/code]

Now how about this ResolveSubstitutions method? This method is passed the ControllerContext and the unmodified HTML output. If no substitution tags are found, it returns immediately. Otherwise, a regular expression is fired off which will perform replaces depending on the contents of this substitution variable.

One thing to note here is that this is actually a nice security hole! Be sure to ALWAYS Html.Encode() dynamic data, as users can inject these substitution tags easily in your dynamic pages and possibly receive useful error messages with context information...

[code:c#]

private string ResolveSubstitutions(ControllerContext filterContext, string source)
{
    // Any substitutions?
    if (source.IndexOf("<!--SUBSTITUTION:") == -1)
    {
        return source;
    }

    // Setup regular expressions engine
    MatchEvaluator replaceCallback = new MatchEvaluator(
        matchToHandle =>
        {
            // Replacements
            string tag = matchToHandle.Value;

            // Parts
            string[] parts = tag.Split(':');
            string className = parts[1];
            string methodName = parts[2].Replace("-->", "");

            // Execute method
            Type targetType = Type.GetType(className);
            MethodInfo targetMethod = targetType.GetMethod(methodName, BindingFlags.Static | BindingFlags.Public);
            return (string)targetMethod.Invoke(null, new object[] { filterContext });
        }
    );
    Regex templatePattern = new Regex(@"<!--SUBSTITUTION:[A-Za-z_\.]+:[A-Za-z_\.]+-->", RegexOptions.Multiline);

    // Fire up replacement engine!
    return templatePattern.Replace(source, replaceCallback);
}

[/code]

How easy was all that? You can download the full soure and an example here.

kick it on DotNetKicks.com

Creating an ASP.NET MVC OutputCache ActionFilterAttribute

In every web application, there are situations where you want to cache the HTML output of a specific page for a certain amount of time, because underlying data and processing isn't really subject to changes a lot. This cached response is stored in the web server's memory and offers very fast responses because no additional processing is required.

Using "classic" ASP.NET, one can use the OutputCache directive on a .aspx page to tell the ASP.NET runtime to cache the response data for a specific amount of time. Optionally, caching may vary by parameter, which results in different cached responses depending on the parameters that were passed in the URL.

As an extra feature, one can also send some HTTP headers to the client and tell him to load the page from  the web browser's cache until a specific amount of time has passed. Big advantage of this is that your web server will receive less requests from clients because they simply use their own caching.

Using the ASP.NET MVC framework (preview 3, that is), output caching is still quite hard to do. Simply specifying the OutputCache directive in a view does not do the trick. Luckily, there's this thing called an ActionFilterAttribute, which lets you run code before and after a controller action executes. This ActionFilterAttribute class provides 4 extensibility points:

  • OnActionExecuting occurs just before the action method is called
  • OnActionExecuted occurs after the action method is called, but before the result is executed (before the view is rendered)
  • OnResultExecuting occurs just before the result is executed (before the view is rendered)
  • OnResultExecuted occurs after the result is executed (after the view is rendered)

Let's use this approach to create an OutputCache ActionFilterAttribute which allows you to decorate any controller and controller action, i.e.:

[code:c#]

[OutputCache(Duration = 60, VaryByParam = "*", CachePolicy = CachePolicy.Server)]
public ActionResult Index()
{
    // ...
}

[/code]

We'll be using an enumeration called CachePolicy to tell the OutputCache attribute how and where to cache:

[code:c#]

public enum CachePolicy
{
    NoCache = 0,
    Client = 1,
    Server = 2,
    ClientAndServer = 3
}

[/code]

1. Implementing client-side caching

Actually, this one's really easy. Right before the view is rendered, we'll add some HTTP headers to the response stream. The web browser will receive these headers and respond to them by using the correct caching settings. If we pass in a duration of 60, the browser will cache this page for one minute.

[code:c#]

public class OutputCache : ActionFilterAttribute
{
    public int Duration { get; set; }
    public CachePolicy CachePolicy { get; set; }

    public override void OnActionExecuted(ActionExecutedContext filterContext)
    {
        // Client-side caching?
        if (CachePolicy == CachePolicy.Client || CachePolicy == CachePolicy.ClientAndServer)
        {
            if (Duration <= 0) return;

            HttpCachePolicyBase cache = filterContext.HttpContext.Response.Cache;
            TimeSpan cacheDuration = TimeSpan.FromSeconds(Duration);

            cache.SetCacheability(HttpCacheability.Public);
            cache.SetExpires(DateTime.Now.Add(cacheDuration));
            cache.SetMaxAge(cacheDuration);
            cache.AppendCacheExtension("must-revalidate, proxy-revalidate");
        }
    }
}

[/code]

2. Implementing server-side caching

Server-side caching is a little more difficult, because there's some "dirty" tricks to use. First of all, we'll have to prepare the HTTP response to be readable for our OutputCache system. To do this, we first save the current HTTP context in a class variable. Afterwards, we set up a new one which writes its data to a StringWriter that allows reading to occur:

[code:c#]

existingContext = System.Web.HttpContext.Current;
writer = new StringWriter();
HttpResponse response = new HttpResponse(writer);
HttpContext context = new HttpContext(existingContext.Request, response)
{
    User = existingContext.User
};
System.Web.HttpContext.Current = context;

[/code]

Using this in a OnResultExecuting override, the code would look like this:

[code:c#]

public override void OnResultExecuting(ResultExecutingContext filterContext)
{
    // Server-side caching?
    if (CachePolicy == CachePolicy.Server || CachePolicy == CachePolicy.ClientAndServer)
    {
        // Fetch Cache instance
        cache = filterContext.HttpContext.Cache;

        // Fetch cached data
        object cachedData = cache.Get(GenerateKey(filterContext));
        if (cachedData != null)
        {
            // Cache hit! Return cached data
            cacheHit = true;
            filterContext.HttpContext.Response.Write(cachedData);
            filterContext.Cancel = true;
        }
        else
        {
            // Cache not hit.
            // Replace the current context with a new context that writes to a string writer
            existingContext = System.Web.HttpContext.Current;
            writer = new StringWriter();
            HttpResponse response = new HttpResponse(writer);
            HttpContext context = new HttpContext(existingContext.Request, response)
            {
                User = existingContext.User
            };

            // Copy all items in the context (especially done for session availability in the component)
            foreach (var key in existingContext.Items.Keys)
            {
                context.Items[key] = existingContext.Items[key];
            }

            System.Web.HttpContext.Current = context;
        }
    }
}

[/code]

By using this code, we can retrieve an existing item from cache and set up the HTTP response to be read from. But what about storing data in the cache? This will have to occur after the view has rendered:

[code:c#]

public override void OnResultExecuted(ResultExecutedContext filterContext)
{
    // Server-side caching?
    if (CachePolicy == CachePolicy.Server || CachePolicy == CachePolicy.ClientAndServer)
    {
        if (!cacheHit)
        {
            // Restore the old context
            System.Web.HttpContext.Current = existingContext;

            // Return rendererd data
            existingContext.Response.Write(writer.ToString());

            // Add data to cache
            cache.Add(
                GenerateKey(filterContext),
                writer.ToString(),
                null,
                DateTime.Now.AddSeconds(Duration),
                Cache.NoSlidingExpiration,
                CacheItemPriority.Normal,
                 null);
        }
    }
}

[/code]

Now you noticed I added a VaryByParam property to the OutputCache ActionFilterAttribute. When caching server-side, I can use this to vary cache storage by the parameters that are passed in. The GenerateKey method will actually generate a key depending on controller, action and the VaryByParam value:

[code:c#]

private string GenerateKey(ControllerContext filterContext)
{
    StringBuilder cacheKey = new StringBuilder();

    // Controller + action
    cacheKey.Append(filterContext.Controller.GetType().FullName);
    if (filterContext.RouteData.Values.ContainsKey("action"))
    {
        cacheKey.Append("_");
        cacheKey.Append(filterContext.RouteData.Values["action"].ToString());
    }

    // Variation by parameters
    List<string> varyByParam = VaryByParam.Split(';').ToList();

    if (!string.IsNullOrEmpty(VaryByParam))
    {
        foreach (KeyValuePair<string, object> pair in filterContext.RouteData.Values)
        {
            if (VaryByParam == "*" || varyByParam.Contains(pair.Key))
            {
                cacheKey.Append("_");
                cacheKey.Append(pair.Key);
                cacheKey.Append("=");
                cacheKey.Append(pair.Value.ToString());
            }
        }
    }

    return cacheKey.ToString();
}

[/code]

There you go! Now note that you can add this OutputCache attribute to any controller and any controller action you  have in your application. The full source code is available for download here: MvcCaching.zip (211.33 kb) (full sample) or OutputCache.zip (1.59 kb) (only attribute).

UPDATE: Make sure to read part 2, available here.

kick it on DotNetKicks.com

The devil is in the details (Visual Studio Team System test policy)

Have you ever been in a difficult situation where a software product is overall very good, but a small detail is going wrong? At least I've been, for the past week...

Team System allows check-in policies to be enforced prior to checking in your code. One of these policies is the unit testing policy, which allows you to enforce a specific test list to be run prior to checking in your code.

How it is...

Now here's the catch: what if you have a Team Project with 2 solutions in it? How can I enforce the check-in policy to run tests from solution A only when something in solution A is checked in, tests from solution B with solution B changes, ...

How it should be...

Creating a custom check-in policy

To be honest, there actually are quite enough examples on creating a custom check-in policy and how to install them. So I'll keep it short: here's the source code of my solution (VS2008 only).

kick it on DotNetKicks.com

Code based ASP.NET MVC GridView

ASP.NET MVC GridViewEarlier this week a colleague of mine asked me if there was such thing as a  DataGrid or GridView or something like that in the ASP.NET MVC framework. My first answer was: "Nope!". I advised him to look for a nice foreach implementation or using ExtJS, Dojo or similar. Which made me think... Why not create a simple GridView extension method which generates a nice looking, plain-HTML grid with all required features like paging, editing, deleting, alternating rows, ...?

The idea was simple: an extension method to the HtmlHelper class would be enough. Required parameters: a header and footer template, item template, edit item template, ... But how to pass in these templates using a simple C# parameter... Luckily,  C# 3.0 introduced lambdas! Why? They are super-flexible and versatile! For instance, take the following code:

[code:c#]

// C# code:
public void RenderPerson(Person p, Action<T> renderMethod) {
    renderMethod(p);
}

// ASP.NET code:
<% RenderPerson(new Person(), person => { %>
    Hello! You are <%=person.Name%>.
<% } %>

[/code]

It translates nicely into:

[code:c#]

Response.Write("Hello! You are Maarten.");

[/code]

Creating a GridView extension method should not be that hard! And it sure isn't.

Live demo

Perhaps I should put this last in my blog posts, but there are always people who are only reading the title and downloading an example:

1. The GridView extension method

Quite short and quite easy:

[code:c#]

public static class GridViewExtensions
{
    public static void GridView<T>(
        this HtmlHelper html,
        GridViewData<T> data,
        Action<GridViewData<T>> headerTemplate,
        Action<T, string> itemTemplate,
        string cssClass,
        string cssAlternatingClass,
        Action<T> editItemTemplate,
        Action<GridViewData<T>> footerTemplate)
    {
        headerTemplate(data);

        int i = 0;
        foreach (var item in data.PagedList)
        {
            if (!item.Equals(data.EditItem))
            {
                itemTemplate(item, (i % 2 == 0 ? cssClass : cssAlternatingClass));
            }
            else
            {
                editItemTemplate(item);
            }

            i++;
        }

        footerTemplate(data);
    }
}

[/code]

2. GridViewData

Of couse, data will have to be displayed. And we'll need a property which sets the current item being edited. Here's my Model I'll be passing to the View:

[code:c#]

public class GridViewData<T>
{
    public PagedList<T> PagedList { get; set; }

    public T EditItem { get; set; }
}

[/code]

By the way, the PagedList<T> I'm using is actually a shameless copy from Rob Conery's blog a while ago.

3. The View

Of course, no rendered HTML without some sort of View. Here's a simplified version in which I pass the GridView<T> extension method the required data, header template, item template, edit item template and footer template. Also noice the alternating rows are simply alternating CSS styles (item and item-alternating).

[code:c#]

<%Html.GridView<Employee>(
    this.ViewData.Model,
    data => { %>
        <table class="grid" cellpadding="0" cellspacing="0">
    <% },
    (item, css) => { %>
        <tr class="<%=css%>">
            <td><%=Html.ActionImage<HomeController>(c => c.Edit(item.Id), "~/Content/edit.gif", "Edit", null)%></td>
            <td><%=Html.ActionImage<HomeController>(c => c.Delete(item.Id), "~/Content/delete.gif", "Delete", null)%></td>
            <td>&nbsp;</td>
            <td><%=item.Name%></td>
            <td><%=item.Email%></td>
        </tr>
    <% },
    "item",
    "item-alternating",
    item => { %>
        <%using (Html.Form<HomeController>(c => c.Save(item.Id), FormMethod.Post, new { id = "editForm" })) {%>
            <tr class="item-edit">
                <td><%=Html.SubmitImage("save", "~/Content/ok.gif", new { alt = "Update" })%></td>
                <td><%=Html.ActionImage<HomeController>(c => c.Index(), "~/Content/cancel.gif", "Cancel", null)%></td>
                <td>&nbsp;</td>
                <td><%=Html.TextBox("Name", item.Name)%></td>
                <td><%=Html.TextBox("Email", item.Email)%></td>
            </tr>
        <% } %>
    <% },
    data => { %>
        </table>
<% });%>

[/code]

4. The Controller

The Controller is perhaps the hardest part: it contains all methods that handle actions which are requested by the View. I have a Show action which simply shows the View with current data. Also, I have implemented an Edit and Save action. Make sure to check my example code download for the full example (earlier in this post).

[code:c#]

// ...

public ActionResult Show(int? page)
{
    CurrentPage = page.HasValue ? page.Value : CurrentPage;
    GridViewData<Employee> viewData = new GridViewData<Employee>
    {
        PagedList = Employees.ToPagedList<Employee>(CurrentPage, 4)
    };

    return View("Index", viewData);
}

public ActionResult Edit(int id)
{
    GridViewData<Employee> viewData = new GridViewData<Employee>
    {
        PagedList = Employees.ToPagedList<Employee>(CurrentPage, 4),
        EditItem = Employees.Where( e => e.Id == id).FirstOrDefault()
    };

    return View("Index", viewData);
}

public ActionResult Save(int id)
{
    BindingHelperExtensions.UpdateFrom(
        Employees.Where(e => e.Id == id).FirstOrDefault(),
        Request.Form
    );
    return RedirectToAction("Show");
}

// ...

[/code]

Note: based on ASP.NET MVC preview 3

kick it on DotNetKicks.com 

Detailed code metrics with NDepend

A while ago, I blogged about code performance analysis in Visual Studio 2008. Using profiling and hot path tracking, I measured code performance and was able to react to that. Last week, Patrick Smacchia contacted me asking if I wanted to test his project NDepend. He promised me NDepend would provide more insight in my applications. Let's test that!

After downloading, extracting and starting NDepend, an almost familiar interface shows up. Unfortunately, the interface that shows up after analyzing a set of assemblies is a little bit overwhelming... Note that this overwhelming feeling fades away after 15 minutes: the interface shows the information you want in a very efficient way! Here's the analysis of a personal "wine tracking" application I wrote 2 years ago.

Am I independent?

Let's start with the obvious... One of the graphs NDepend generates, is a dependency map. This diagram shows all dependencies of my "WijnDatabase" project.

Dependencies mapped

One thing I can see from this, is that there probably is an assembly too much! WijnDatabase.Classes could be a candidate for merging into WijnDatabase, the GUI project. These dependencies are also shown in the dependency window.

Dependencies mapped

You can see (in the upper right corner) that 38 methods of the WijnDatabase assembly are using 5 members of WijnDatabase.Classes. Left-click this cell, and have more details on this! A diagram of boxes clearly shows my methods in a specific form calling into WijnDatabase.Classes.

More detail on dependencies

In my opinion, these kinds of views are really useful to see dependencies in a project without reading code! The fun part is that you can widen this view and have a full dependency overview of all members of all assemblies in the project. Cool! This makes it possible to check if I should be refactoring into something more abstract (or less abstract). Which is also analysed in the next diagram:

Is my application in the zone of pain?

What you can see here is the following:

  • The zone of pain contains assemblies which are not very extensible (no interfaces, no abstract classes, nor virtual methods, stuff like that). Also, these assemblies tend to have lots of dependent assemblies.
  • The zone of uselessness is occupied by very abstract assemblies which have almost no dependent assemblies.

Most of my assemblies don't seem to be very abstract, dependencies are OK (the domain objects are widely used so more in the zone of pain). Conclusion: I should be doing some refactoring to make assemblies more abstract (or replacable, if you prefer it that way).

CQL - Code Query Language

Next to all these graphs and diagrams, there's another powerful utility: CQL, or Code Query Language. It's sort of a "SQL to code" thing. Let's find out some things about my application...

 

Methods poorly commented

It's always fun to check if there are enough comments in your code. Some developers tend to comment more than writing code, others don't write any comments at all. Here's a (standard) CQL query:

[code:c#]

// <Name>Methods poorly commented</Name>
WARN IF Count > 0 IN SELECT TOP 10 METHODS WHERE PercentageComment < 20 AND NbLinesOfCode > 10  ORDER BY PercentageComment ASC
// METHODS WHERE %Comment < 20 and that have at least 10 lines of code should be more commented.
// See the definition of the PercentageComment metric here http://www.ndepend.com/Metrics.aspx#PercentageComment

[/code]

This query searches the top 10 methods containing more than 10 lines of code where the percentage of comments is less than 20%.

CQL result 

Good news! I did quite good at commenting! The result of this query shows only Visual Studio generated code (the InitializeComponent() sort of methods), and some other, smaller methods I wrote myself. Less than 20% of comments in a method consisting of only 11 lines of code (btnVoegItemToe_Click in the image) is not bad!

Quick summary of methods to refactor

Another cool CQL query is the "quick summary of methods to refactor". Only one method shows up, but I should probably refactor it. Quiz: why?

CQL result

Answer: there are 395 IL instructions in this method (and if I drill down, 57 lines of code). I said "probably", because it might be OK after all. But if I drill down, I'm seeing some more information that is probably worrying: cyclomatic complexity is high, there are many variables used, ... Refactoring is indeed the answer for this method!

Methods that use boxing/unboxing

Are you familiar with the concept of boxing/unboxing? If not, check this article. One of the CQL queries in NDepend is actually finding all methods using boxing and unboxing. Seems like my data access layer is boxing a lot! Perhaps some refactoring could be needed in here too.

CQL result

Conclusion

Over the past hour, I've been analysing only a small tip of information from my project. But there's LOTS more information gathered by NDepend! Too much information, you think? Not sure if a specific metric should be fitted on your application? There's good documentation on all metrics as well as short, to-the-point video demos.

In my opinion, each development team should be gathering some metrics from NDepend with every build and do a more detailed analysis once in a while. This detailed analysis will give you a greater insight on how your assemblies are linked together and offer a great review of how you can improve your software design. Now grab that trial copy!

kick it on DotNetKicks.com