Maarten Balliauw {blog}

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

NAVIGATION - SEARCH

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

ASP.NET MVC framework preview 3 released!

Don't know how I do it, but I think this blog post is yet again the first one out there mentioning a new release of the ASP.NET framework (preview 3) Cool

The official installation package can be downloaded from the Microsoft site. Source code is also available from CodePlex.

Update instructions from preview 2 to preview 3 are contained in the download. If you created a project based on the "preview-preview" version, here's what you'll have to update:

  • Controller
    The return of a controller action is no longer returned using RenderView(...), but View(...). Alternative return values are View (returns a ViewResult instance), Redirect (redirects to the specified URL and returns a RedirectResult instance), RedirectToAction, RedirectToRoute, Json (returns a JsonResult instance) and Content (which sends text content to the response and returns a ContentResult instance)
  • View
    I had to update my ViewData calls as the ViewData property of ViewPage<T> is no longer replaced by T. Instead, you'll have to access your model trough ViewData.Model (where Model is of T).
  • Routing
    Routes can now be created using the RouteCollection's extension method "MapRoute". Also, a new constraint has been added to routing where you can allow/disallow specific HTTP methods (i.e. no GET).
  • Version of assemblies
    The versions of the System.Web.Abstractions and System.Web.Routing assemblies that are included with the MVC project template have been changed to version 0.0.0.0 to make sure no conflict occurs with the latest .NET Framework 3.5 SP1 Beta release.

kick it on DotNetKicks.com

Creating a custom ViewEngine for the ASP.NET MVC framework

Have you ever seen a presentation of ScottGu about the ASP.NET MVC framework? There is one particular slide that keeps coming back, stating that every step in the ASP.NET MVC life cycle is pluggable. Let's find out if replacing one of these components is actually easy by creating a custom ViewEngine and corresponding view.

 ASP.NET MVC life cycle

Some background

After a route has been determined by the route handler, a Controller is fired up. This Controller sets ViewData, which is afterwards passed into the ViewEngine. In short, the ViewEngine processes the view and provides the view with ViewData from the Controller. Here's the base class:

[code:c#]

public abstract class ViewEngineBase {
     public abstract void RenderView(ViewContext viewContext);
}

[/code]

By default, the ASP.NET MVC framework has a ViewEngine named WebFormsViewEngine. As the name implies, this WebFormsViewEngine is used to render a view which is created using ASP.NET web forms.

The MvcContrib project contains some other ViewEngine implementations like NVelocity, Brail, NHaml, XSLT, ...

What we are going to build...

Rendered viewIn this blog post, we'll build a custom ViewEngine which will render a page like you see on the right from a view with the following syntax:

[code:c#]

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" >
<head>
    <title>Custom ViewEngine Demo</title>
</head>
<body>
    <h1>{$ViewData.Title}</h1>
    <p>{$ViewData.Message}</p>
    <p>The following fruit is part of a string array: {$ViewData.FruitStrings[1]}</p>
    <p>The following fruit is part of an object array: {$ViewData.FruitObjects[1].Name}</p>
    <p>Here's an undefined variable: {$UNDEFINED}</p>
</body>
</html>

[/code]

What do we need?

First of all, download the current ASP.NET MVC framework from CodePlex. After creating a new ASP.NET MVC web site, tweak some stuff:

  • Remove /Views/*.*
  • Remove /Content/*.* (unless you want to keep the default CSS files)
  • Add a folder /Code

In order to create a ViewEngine, we will have to do the following:

  • Create a default IControllerFactory which sets the ViewEngine we will create on each Controller
  • Edit Global.asax.cs and register the default controller factory
  • Create a ViewLocator (this one will map a controller + action to a specific file name that contains the view to be rendered)
  • Create a ViewEngine (the actual purpose of this blog post)

Let's do some coding!

1. Creating and registering the IControllerFactory implementation

Of course, ASP.NET MVC has a default factory which creates a Controller instance for each incoming request. This factory takes care of dependency injection, including Controller initialization and the assignment of a ViewEngine. Since this is a good point of entry to plug our own ViewEngine in, we'll create an inherited version of the DefaultControllerFactory:

[code:c#]

public class SimpleControllerFactory : DefaultControllerFactory
{
    protected override IController CreateController(RequestContext requestContext, string controllerName)
    {
        Controller controller = (Controller)base.CreateController(requestContext, controllerName);
        controller.ViewEngine = new SimpleViewEngine(); // <-- will be implemented later in this post
        return controller;
    }
}

[/code]

In order to make this SimpleControllerFactory the default factory, edit the Global.asax.cs file and add the following line of code in the Application_Start event:

[code:c#]

ControllerBuilder.Current.SetControllerFactory(typeof(SimpleControllerFactory));

[/code]

2. Create a ViewLocator

In order for the ViewEngine we'll build to find the correct view for each controller + action combination, we'll have to implement a ViewLocator too:

[code:c#] 

public class SimpleViewLocator : ViewLocator
{
    public SimpleViewLocator()
    {
        base.ViewLocationFormats = new string[] { "~/Views/{1}/{0}.htm",
                                                  "~/Views/{1}/{0}.html",
                                                  "~/Views/Shared/{0}.htm",
                                                  "~/Views/Shared/{0}.html"
        };
        base.MasterLocationFormats = new string[] { "" };
    }
}

[/code]

We are actually providing the possible application paths where a view can be stored.

3. Create a ViewEngine

The moment you have been waiting for! The IViewEngine interface requires the following class structure:

[code:c#]

public class SimpleViewEngine : IViewEngine
{
    // ...
    // Private member: IViewLocator _viewLocator = null;
    // Public property: IViewLocator ViewLocator
    // ...

    #region IViewEngine Members

    public void RenderView(ViewContext viewContext)
    {
        string viewLocation = ViewLocator.GetViewLocation(viewContext, viewContext.ViewName);
        if (string.IsNullOrEmpty(viewLocation))
        {
            throw new InvalidOperationException(string.Format("View {0} could not be found.", viewContext.ViewName));
        }

        string viewPath = viewContext.HttpContext.Request.MapPath(viewLocation);
        string viewTemplate = File.ReadAllText(viewPath);

        IRenderer renderer = new PrintRenderer();
        viewTemplate = renderer.Render(viewTemplate, viewContext);

        viewContext.HttpContext.Response.Write(viewTemplate);
    }

    #endregion
}

[/code]

Note that we first locate the view using the ViewLocator, map it to a real path on the server and then render contents directly to the HTTP response. The PrintRenderer class maps {$....} strings in the view to a real variable from ViewData. If you want to see the implementation, please check the download of this example.

Conclusion

Conclusions Replacing the default ViewEngine with a custom made version is actually quite easy! The most difficult part in creating your own ViewEngine implementation will probably be the parsing of your view. Fortunately, there are some examples around which may be a good source of inspiration (see MvcContrib).

If someone wants to use the code snippets I posted to create their own PHP Smarty, please let me know! Smarty is actually quite handy, and might also be useful in ASP.NET MVC.

And yes, it has been a lot of reading, but I did not forget. Download the example code from this blog post: CustomViewEngine.zip (213.17 kb)

kick it on DotNetKicks.com

ASP.NET MVC custom ActionResult (ImageResult)

The ASP.NET MVC framework introduces the concept of returning an ActionResult in Controllers since the "preview preview" release on CodePlex. The purpose of this concept is to return a generic ActionResult object for each Controller method, allowing different child classes returning different results.

An example ActionResult (built-in) is the RenderViewResult. Whenever you want to render a view, you can simply return an object of this class which will render a specific view in its ExecuteResult method. Another example is the HttpRedirectResult which will output an HTTP header (Location: /SomethingElse.aspx).

In my opinion, this is a great concept, because it allows you to develop ASP.NET MVC applications more transparently. In this blog post, I will build a custom ActionResult class which will render an image to the HTTP response stream.

ASP.NET MVC Custom ActionResultAs an example, let's create a page which displays the current time as an image.

One option for implementing this would be creating an ASP.NET HttpHandler which renders this image and can be used inline with a simple HTML tag: <img src="DisplayTime.ashx" />

Wouldn't it be nice to be able to do something more ASP.NET MVC-like? Let's consider the following: <%=Html.Image<HomeController>(c => c.DisplayTime(), 200, 50, "Current time")%>

1. Creating the necessary HtmlHelper extension methods

The above example code is not available in standard ASP.NET MVC source code. We'll need an extension method for this, which will map a specific controller action, width, height and alternate text to a standard HTML image tag:

[code:c#]

public static class ImageResultHelper
{
    public static string Image<T>(this HtmlHelper helper, Expression<Action<T>> action, int width, int height)
        where T : Controller
    {
        return ImageResultHelper.Image<T>(helper, action, width, height, "");
    }

    public static string Image<T>(this HtmlHelper helper, Expression<Action<T>> action, int width, int height, string alt)
        where T : Controller
    {
        string url = helper.BuildUrlFromExpression<T>(action);
        return string.Format("<img src=\"{0}\" width=\"{1}\" height=\"{2}\" alt=\"{3}\" />", url, width, height, alt);
    }

[/code]

2. The custom ActionResult class

Our new ImageResult class will inherit the abstract class ActionResult and implement its ExecuteResult method. This method basically performs communication over the HTTP response stream.

[code:c#]

public class ImageResult : ActionResult
{
    public ImageResult() { }

    public Image Image { get; set; }
    public ImageFormat ImageFormat { get; set; }

    public override void ExecuteResult(ControllerContext context)
    {
        // verify properties
        if (Image == null)
        {
            throw new ArgumentNullException("Image");
        }
        if (ImageFormat == null)
        {
            throw new ArgumentNullException("ImageFormat");
        }

        // output
        context.HttpContext.Response.Clear();

        if (ImageFormat.Equals(ImageFormat.Bmp)) context.HttpContext.Response.ContentType = "image/bmp";
        if (ImageFormat.Equals(ImageFormat.Gif)) context.HttpContext.Response.ContentType = "image/gif";
        if (ImageFormat.Equals(ImageFormat.Icon)) context.HttpContext.Response.ContentType = "image/vnd.microsoft.icon";
        if (ImageFormat.Equals(ImageFormat.Jpeg)) context.HttpContext.Response.ContentType = "image/jpeg";
        if (ImageFormat.Equals(ImageFormat.Png)) context.HttpContext.Response.ContentType = "image/png";
        if (ImageFormat.Equals(ImageFormat.Tiff)) context.HttpContext.Response.ContentType = "image/tiff";
        if (ImageFormat.Equals(ImageFormat.Wmf)) context.HttpContext.Response.ContentType = "image/wmf";

        Image.Save(context.HttpContext.Response.OutputStream, ImageFormat);
    }
}

[/code]

3. A "DisplayTime" action on the HomeController

We'll add a DisplayTime action on the HomeController class, which will return an instance of the newly created class ImageResult:

[code:c#]

public ActionResult DisplayTime()
{
    Bitmap bmp = new Bitmap(200, 50);
    Graphics g = Graphics.FromImage(bmp);

    g.FillRectangle(Brushes.White, 0, 0, 200, 50);
    g.DrawString(DateTime.Now.ToShortTimeString(), new Font("Arial", 32), Brushes.Red, new PointF(0, 0));

    return new ImageResult { Image = bmp, ImageFormat = ImageFormat.Jpeg };
}

[/code]

And just to be complete, here's the markup of the index view on the HomeController:

[code:c#]

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

<asp:Content ID="indexContent" ContentPlaceHolderID="MainContent" runat="server">
    <p>
        <%=Html.Image<HomeController>(c => c.DisplayTime(), 200, 50, "Current time")%>
    </p>
</asp:Content>

[/code]

Want the source code? Download it here! You can use it with the current ASP.NET MVC framework source code drop.

kick it on DotNetKicks.com  

Having fun with project managers! (or: why you should lock your desktop)

Have you ever left your computer unattended? (I do hope that!) Do you lock your desktop when this occurs? (I hope so either!) Not locking your desktop can be a risk. Your data might get stolen, someone might install a trojan, ...

My project manager is one of those people who do not lock their computer when away. After having explained that this is a very simple thing he still seems to forget it everytime. Since a few weeks, we are using some more persuasive manners to teach him to press the Windows-key + L whenever he leaves his computer. Some practical jokes...

1. Are those icons?

  • Create a screenshot of the user's desktop
  • Set this screenshot as the desktop background
  • Remove all real icons from the user's desktop (copy them somewhere to cause no real harm!)
  • Wait till the user gets back and starts clicking on his desktop "icons"... NOT Cool

2. Cake for everyone!

  • Open the user's e-mail client
  • Send a message to the entire team saying "I'm treating everyone with cake!" (other option: "I'm buying a beer for everyone!")
  • Keep reminding the user of his nice e-mail Cool

3. Right-click here...

Here's a simple one: switch the user's mouse buttons (control panel - mouse properties - buttons). Also press the CTRL + ALT + Down-arrow. If you're lucky and the user has an Intl graphics card, his desktop will also be upside down.

4. www.website.com...

  • If you have administrative rights on the user's PC, find and edit his host file (c:\windows\system32\drivers\etc\hosts)
  • Add a new entry which map his favourite website to something completely different. For example, map www.google.com to the IP address 208.77.188.166.

5. Other funny things

We've tried the above pranks, but no luck yet... Locking his desktop remains something that's not important to him (probably because he always has to laugh with practical jokes). Here's a list of some other jokes we might try in the near future...

Any other suggestions, preferrably not harmful? Please let me know!

kick it on DotNetKicks.com

Finally! My own custom BlogEngine.net template!

Right after my blog move to BlogEngine.net, I decided to create a custom template for my blog to make it more unique in the blogosphere. Less than a year later (11 months, to be precise) I found the time to fire up my HTML and image editor to create a new look for my blog. Now who said developers are lazy?

Old blog look New blog look