Archive for the ‘ASP.NET MVC’ Category

A different approach to dependency injection with ASP.NET MVC

No Comments »

If you read about dependency injection and ASP.NET MVC, what people are usually describing is constructor injection: all the dependencies of an ASP.NET MVC controller are provided through the constructor.

This has been bugging me for a while.  The problem is that, when you do traditional constructor injection, you end up with something like this:

public class HomeController : Controller
{
  public HomeController(IServiceA serviceA, IServiceB serviceB, IServiceC serviceC)
  {
    /* … */
  }

  public ActionResult ActionA()

  {
    /* uses serviceA */
  }

  public ActionResult ActionB()
  {
    /* uses serviceB */

  }

  public ActionResult ActionC()

  {
    /* uses serviceC */
  }
}

Notice how you end up having to pass all dependencies for all actions to the controller’s constructor. As the list of dependencies grows longer, and all these dependencies have to be instantiated every time ASP.NET MVC invokes an action, performance can suffer. Moreover, all those dependencies make writing unit tests tiresome, hurting developer productivity and code quality.

One way to mitigate the problem is to pass null or a dumb stub for the dependencies not used by a particular action under test. In other words, when testing ActionA(), just pass null or a stub for depencies serviceB and serviceC when instantiating the controller.

However, you can only do this because you are using implicit knowledge about the inner workings of ActionA(). If you change the implementation of the action, the dependencies might also change. In that case, everything will look the same from the outside, but unit tests will start failing.

So, ideally, what we want when testing an action method is to

  • only have to provide the dependencies actually used by the action method
  • have the dependencies of each action method be explicitly stated

Looking at this, it looks like what we want is to be able to write something like this:

public class HomeController : Controller
{
  public ActionResult ActionA(IServiceA serviceA)
  {
    /* uses serviceA */
  }

  public ActionResult ActionB(IServiceB serviceB)
  {
    /* uses serviceB */
  }

  public ActionResult ActionC(IServiceC serviceC)
  {
    /* uses serviceC */
  }

}

Here

  • the dependencies  of each action are very explicitly stated.
  • when unit testing an action method, we only have to provide the actual dependencies of that method.
  • as ASP.NET MVC has fewer dependencies to instantiate, performance is improved.

However, actions are invoked by the ASP.NET MVC framework, which knows nothing of our services, so how do we make this work?

As you probably know, ASP.NET MVC has a mechanism for providing arguments to action methods: model binding. In the default setup, model binding is used to turn HTTP data like query parameter values and form input into action method arguments. Fortunately, the model binding architecture is extensible. Thus, we can create our own model binder to provide the service dependencies:

public class DependencyResolvingModelBinder : DefaultModelBinder
{
  public override object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
  {
    return ShouldBeResolvedFromIOCContainer(bindingContext)
        ? DependencyResolver.Current.GetService(bindingContext.ModelType)
        : base.BindModel(controllerContext, bindingContext);
  }

  private static bool ShouldBeResolvedFromIOCContainer(ModelBindingContext bindingContext)
  {
    return bindingContext.ModelType.IsInterface;
  }
}

When this model binder is asked to provide a value for an action method argument that is an interface, it asks the IOC container to provide the value. In all other cases, it just falls back to the default ASP.NET MVC behaviour.

Configure ASP.NET MVC to use the new model binder by calling

ModelBinders.Binders.DefaultBinder = new DependencyResolvingModelBinder();

at the start of your application and presto! With just the few lines of code above, you can now write nice, clean action methods with well defined dependencies.

Enjoy writing those tests!


Is the Web Application Toolkit for Freemium Applications from Microsoft any good?

No Comments »

Microsoft recently released the “Web Application Toolkit for Freemium Applications” for building applications based on the freemium model. I am not a big fan of the Freemium model myself, but I thought it would be interesting to take a closer look at the toolkit anyway.

First off, let’s recap the freemium model. According to Wikipedia:

Freemium is a business model that works by offering basic Web services, or a basic downloadable digital product, for free, while charging a premium for advanced or special features.

Thus, to build applications for the freemium model, we need to be able to

  1. Have multiple feature sets and manage these
  2. Have users associated with one or more feature sets
  3. Have a user’s chosen feature sets reflect in the application GUI presented to the user
  4. Have a user’s chosen feature sets automatically reflect in the billing

It might also be nice to

  1. have users grouped into categories with catchy names and associate feature sets with each category
  2. be able to easily move users between categories, possibly by self-service

This is what the toolkit should ideally help us achieve.

The tools

In broad terms, the toolkit works on two concepts: features and SKUs, and it uses the ASP.NET Membership functionality to associate users to SKUs. SKUs are sets of features and each SKU has a unique identifier called a slug. SKUs are mapped to users by means of standard ASP.NET Roles having the same name as a SKU slug. Thus, there will be a “Gold” SKU identified by the slug “gold” and all users in the role “gold” are associated to the Gold SKU.

If you buy into this way of grouping features and associating them with users, the framework provides:

  • Page extension methods for displaying/hiding content based on the user’s SKU
  • Action attributes to allow or prevent the user from executing certain actions based on the SKU
  • A MVC 2 Area with controllers and views for managing SKUs
The verdict

The components described in the three bullets above are definitely valuable in any application based on the freemium model. However, their actual implementation leaves something to be desired. The provided functionality isn’t componentized properly and there is a general lack of extension points: to really use the toolkit, you need to buy into a lot of arbitrary design decisions which are unlikely to suit your application. Moreover, there is just a gust of code smell around it: views aren’t properly decoupled from business logic, no DI is used etc.

All in all the Web Application Toolkit for Freemium Applications is a nice initiative from Microsoft and it contains some relevant thoughts. If Microsoft decides to put some effort into the toolkit to provide extension points etc., it may even be able to kickstart your next freemium application. At the moment, however, the toolkit is pretty blunt. It is nothing more than a sample implementation which may provide inspiration but does not deserve to be called a toolkit.

The Web Application Toolkit for Freemium Applications is available on MSDN.


Presenting at the Microsoft Web Partner event Oct. 7th

No Comments »

I will be giving a high-level presentation on the ASP.NET MVC framework at Microsoft’s Web Partner event on October 7th.

Participation is free for web partners. Register here.

Update: The slides for the presentation are now available here.


Writing an Add-in for Visual Studio 2008

4 Comments »

I’ve been thinking about writing a test generation add-in for Visual Studio. The idea is to have an add-in that will enable the user to auto-generate a bunch of tests for each controller in an ASP.NET MVC project.

I haven’t done any programming for Visual Studio before, so I decided that the first step would be to be able to place a menu item in the context menu of the files in Solution Explorer containing controllers.

 vsaddin_enabled vsaddin_disabled

To be able to query the Solution Explorer for things like ‘give me the files the user currently has selected’, ‘does the file the user has selected contain a controller’ etc., I created a wrapper:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using EnvDTE;
using EnvDTE80;
using TestGenerator.CodeGeneration;

namespace TestGenerator
{
    public class SolutionExplorerWrapper
    {
        private DTE2 _applicationObject;

        public SolutionExplorerWrapper(DTE2 applicationObject)
        {
            this._applicationObject = applicationObject;
        }

        public bool IsFolder(ProjectItem item)
        {
            foreach (string guid in folderGUIDs)
            {
                if (item.Kind.ToUpper() == guid)
                    return true;
            }
            return false;
        }

        public IEnumerable<ProjectItem> GetSelectedProjectItems()
        {
            UIHierarchy uih = (UIHierarchy)_applicationObject.Windows.Item(Constants.vsWindowKindSolutionExplorer).Object;
            Array selectedItems = (Array)uih.SelectedItems;
            foreach (UIHierarchyItem item in selectedItems)
                yield return (ProjectItem)item.Object;
        }

        public IEnumerable<ProjectItem> GetSelectedItemsContainingControllers()
        {
            return (from pi in GetSelectedProjectItems()
                    where
                        pi.IsPhysicalFile() &&
                        ContainsControllers((FileCodeModel2)pi.FileCodeModel)
                    select pi);
        }



        private bool ContainsControllers(FileCodeModel2 file)
        {
            FileCodeModel2QueryHelper helper = new FileCodeModel2QueryHelper(file);
            return helper.FileContainsControllers();
        }

        private readonly string[] folderGUIDs = new string[3] { "{6BB5F8EF-4483-11D3-8BCF-00C04F8EC28C}", "{6BB5F8F0-4483-11D3-8BCF-00C04F8EC28C}", "{66A26722-8FB5-11D2-AA7E-00C04F688DDE}" };
    }
}

The elements in the Solution Explorer are represented as ProjectItem objects and because of all the COM goo (I guess) you can’t just do something like

if(projectItem is IFolder)

to determine if a given ProjectItem instance is a folder. Thus, to ease the interaction with these objects, I created two extension methods:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using EnvDTE;

namespace TestGenerator
{
    public static class ProjectItemExtensions
    {
        public static bool IsPhysicalFile(this ProjectItem item)
        {
            return item.Kind == EnvDTE.Constants.vsProjectItemKindPhysicalFile;
        }

        public static bool IsPhysicalFolder(this ProjectItem item)
        {
            return item.Kind == EnvDTE.Constants.vsProjectItemKindPhysicalFolder;
        }
    }
}

Given a COM object like a ProjectItem, you can use a class in the Microsoft.VisualBasic namespace to get a string representation of what type of object the COM object is wrapping. Hence, I created the following extension method, which was very helpful during development:

public static class ComObjectExtentions
{
    public static string GetTypeName(this object o)
    {
        return Microsoft.VisualBasic.Information.TypeName(o);
    }
}

The actual implementation of the add-in looks complex at first glance, but it is really quite simple. Much of the code is just boilerplate code generated by the Visual Studio Add-In wizard.

public class Connect : IDTExtensibility2, IDTCommandTarget
    {
        /// <summary>Implements the constructor for the Add-in object. Place your initialization code within this method.</summary>
        public Connect()
        {
        }

        /// <summary>Implements the OnConnection method of the IDTExtensibility2 interface. Receives notification that the Add-in is being loaded.</summary>
        /// <param term='application'>Root object of the host application.</param>
        /// <param term='connectMode'>Describes how the Add-in is being loaded.</param>
        /// <param term='addInInst'>Object representing this Add-in.</param>
        /// <seealso class='IDTExtensibility2' />
        public void OnConnection(object application, ext_ConnectMode connectMode, object addInInst, ref Array custom)
        {
            try
            {
                _applicationObject = (DTE2)application;
                _addInInstance = (AddIn)addInInst;

                switch (connectMode)
                {
                    case ext_ConnectMode.ext_cm_UISetup:

                        // Create commands in the UI Setup phase. This phase is called only once when the add-in is deployed.
                        CreateCommands();
                        break;

                    case ext_ConnectMode.ext_cm_AfterStartup:

                        InitializeAddIn();
                        break;

                    case ext_ConnectMode.ext_cm_Startup:

                        // Do nothing yet, wait until the IDE is fully initialized (OnStartupComplete will be called)
                        break;
                }
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.ToString(), "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
            }
        }

        private void InitializeAddIn()
        {
            CommandBarControl myCommandBarControl;
            CommandBar codeWindowCommandBar;
            Command myCommand1;
            CommandBars commandBars;

            // Retrieve commands created in the ext_cm_UISetup phase of the OnConnection method
            myCommand1 = _applicationObject.Commands.Item(_addInInstance.ProgID + "." + m_COMMAND_GENERATETESTS_NAME, -1);

            // Retrieve the context menu of an item in solution explorer
            commandBars = (CommandBars)_applicationObject.CommandBars;
            codeWindowCommandBar = commandBars["Item"];

            // Add a popup command bar
            myCommandBarControl = codeWindowCommandBar.Controls.Add(MsoControlType.msoControlPopup,
               System.Type.Missing, System.Type.Missing, System.Type.Missing, System.Type.Missing);

            m_commandBarPopup = (CommandBarPopup)myCommandBarControl;

            // Change its caption
            m_commandBarPopup.Caption = "RUI Test Generator";

            // Add controls to the popup command bar
            m_commandBarControl1 = (CommandBarControl)myCommand1.AddControl(m_commandBarPopup.CommandBar,
               m_commandBarPopup.Controls.Count + 1);

            m_commandBarControl1.Caption = "Generate Tests";
        }

        /// <summary>Implements the OnDisconnection method of the IDTExtensibility2 interface. Receives notification that the Add-in is being unloaded.</summary>
        /// <param term='disconnectMode'>Describes how the Add-in is being unloaded.</param>
        /// <param term='custom'>Array of parameters that are host application specific.</param>
        /// <seealso class='IDTExtensibility2' />
        public void OnDisconnection(ext_DisconnectMode disconnectMode, ref Array custom)
        {
            try
            {
                if (m_commandBarPopup != null)
                {
                    m_commandBarPopup.Delete(true);
                }
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.ToString(), "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
            }
        }

        /// <summary>Implements the OnAddInsUpdate method of the IDTExtensibility2 interface. Receives notification when the collection of Add-ins has changed.</summary>
        /// <param term='custom'>Array of parameters that are host application specific.</param>
        /// <seealso class='IDTExtensibility2' />
        public void OnAddInsUpdate(ref Array custom)
        {
        }

        /// <summary>Implements the OnStartupComplete method of the IDTExtensibility2 interface. Receives notification that the host application has completed loading.</summary>
        /// <param term='custom'>Array of parameters that are host application specific.</param>
        /// <seealso class='IDTExtensibility2' />
        public void OnStartupComplete(ref Array custom)
        {
            InitializeAddIn();
        }

        /// <summary>Implements the OnBeginShutdown method of the IDTExtensibility2 interface. Receives notification that the host application is being unloaded.</summary>
        /// <param term='custom'>Array of parameters that are host application specific.</param>
        /// <seealso class='IDTExtensibility2' />
        public void OnBeginShutdown(ref Array custom)
        {
        }

        /// <summary>Implements the QueryStatus method of the IDTCommandTarget interface. This is called when the command's availability is updated</summary>
        /// <param term='commandName'>The name of the command to determine state for.</param>
        /// <param term='neededText'>Text that is needed for the command.</param>
        /// <param term='status'>The state of the command in the user interface.</param>
        /// <param term='commandText'>Text requested by the neededText parameter.</param>
        /// <seealso class='Exec' />
        public void QueryStatus(string commandName, vsCommandStatusTextWanted neededText, ref vsCommandStatus status, ref object commandText)
        {
            if (commandName == _addInInstance.ProgID + "." + m_COMMAND_GENERATETESTS_NAME)
            {
                SolutionExplorerWrapper solutionExplorer = new SolutionExplorerWrapper(_applicationObject);
                if (solutionExplorer.GetSelectedItemsContainingControllers().Any())
                    status = vsCommandStatus.vsCommandStatusSupported | vsCommandStatus.vsCommandStatusEnabled;
                else
                    status = vsCommandStatus.vsCommandStatusSupported;
            }
        }

        /// <summary>Implements the Exec method of the IDTCommandTarget interface. This is called when the command is invoked.</summary>
        /// <param term='commandName'>The name of the command to execute.</param>
        /// <param term='executeOption'>Describes how the command should be run.</param>
        /// <param term='varIn'>Parameters passed from the caller to the command handler.</param>
        /// <param term='varOut'>Parameters passed from the command handler to the caller.</param>
        /// <param term='handled'>Informs the caller if the command was handled or not.</param>
        /// <seealso class='Exec' />
        public void Exec(string commandName, vsCommandExecOption executeOption, ref object varIn, ref object varOut, ref bool handled)
        {
            if (commandName == _addInInstance.ProgID + "." + m_COMMAND_GENERATETESTS_NAME)
            {
                GenerateTestsForSelectedFiles();
            }
        }

        #region Menu and command setup

        private const string m_COMMAND_GENERATETESTS_NAME = "GenerateTests";
        private const string m_COMMAND_GENERATETESTS_TEXT = "Generate Tests";
        //private const string m_NAME_COMMAND2 = "MyCommand2";

        private CommandBarPopup m_commandBarPopup;
        private CommandBarControl m_commandBarControl1;
        //private CommandBarControl m_commandBarControl2;

        private void CreateCommands()
        {
            object[] contextUIGuids = new object[] { };

            _applicationObject.Commands.AddNamedCommand(_addInInstance, m_COMMAND_GENERATETESTS_NAME, m_COMMAND_GENERATETESTS_TEXT, m_COMMAND_GENERATETESTS_TEXT, true, 59,
               ref contextUIGuids, (int)vsCommandStatus.vsCommandStatusSupported);
        }

        #endregion

        public bool GenerateTestsForSelectedFiles()
        {
            SolutionExplorerWrapper solutionExplorer = new SolutionExplorerWrapper(_applicationObject);
            foreach (ProjectItem projectItem in solutionExplorer.GetSelectedProjectItems())
            {
                if (!projectItem.IsPhysicalFile())
                {
                    MessageBox.Show(string.Format("Cannot generate code for {0}", projectItem.Name), "Project item not supported");
                    return true;
                }

                CodeFileGenerator generator = new CodeFileGenerator();
                generator.GenerateTests(projectItem, _applicationObject.Solution);
            }
            return true;
        }

        private DTE2 _applicationObject;
        private AddIn _addInInstance;
    }

Of particular interest are the methods QueryStatus(…) and Exec(…).

QueryStatus is called whenever Visual Studio wants to know if the add-in should be available for use. As you can see, I just use the SolutionExplorerWrapper to make the add-in available, whenever the user has selected a file containing one or more controllers.

Exec is called whenever a command associated to the add-in is invoked. If the command is the command for generating tests (the add-in might contain other buttons or menu items invoking other commands) I proceed to create tests.

That’s it. My add-in now shows up whenever the user right-clicks a controller in Solution Explorer.

For completeness sake I have made all of the source code available here, including the CodeFileGenerator that I am using for generating (at the moment, very rudimentary) test code.


MVC Model Binding to an interface

No Comments »

Have you ever tried using ASP.NET MVC’s data binding capabilities against an interface? If so, you may have experienced some unexpected behaviour.

Let’s say we’ve got a form with two fields that we want to post to a Create action. In the Create action we’ll usually databind the fields to properties of a business object and persist the object to the database. For this example, we’ll just perform the data binding and display the bound values.

Our Order class is very simple:

public class Order : IOrder
{
    public int Id { get; set; }
    public string Text { get; set; }
}

Our form for submitting an order is equally simple:

<%@ Page Language="C#" MasterPageFile="~/Views/Shared/Site.Master" Inherits="System.Web.Mvc.ViewPage" %>

<asp:Content ID="indexTitle" ContentPlaceHolderID="TitleContent" runat="server">
    Home Page
</asp:Content>

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

     <% using (Html.BeginForm("Create", "Home")){ %>
        <fieldset>
        <legend>Order:</legend>
            Id:<br />
            <%= Html.TextBox("order.Id") %> <br />
            Text:<br />
            <%= Html.TextBox("order.Text") %><br />
        </fieldset>
    <%} %>
</asp:Content>

We’ve got a Create action like this

[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Create(FormCollection form)
{
    Order order = new Order();
    UpdateModel(order, "order", form.ToValueProvider());

    ViewData["Id"] = order.Id;
    ViewData["Text"] = order.Text;

    return View();
}

and a corresponding view which displays the values:

<%@ Page Title="" Language="C#" MasterPageFile="~/Views/Shared/Site.Master" Inherits="System.Web.Mvc.ViewPage" %>

<asp:Content ID="Content1" ContentPlaceHolderID="TitleContent" runat="server">
    Create
</asp:Content>

<asp:Content ID="Content2" ContentPlaceHolderID="MainContent" runat="server">

    <h2>Create</h2>
    <p>Data used for create:</p>
    <p>
        Id: <%= ViewData["Id"] %> <br />
        Text: <%= ViewData["Text"] %>
    </p>


</asp:Content>

When we enter a pair of values like so

forminput

we will see this upon submitting

result1

However, what happens if we try to databind to a reference of type IOrder? The IOrder interface looks like this

public interface IEntity
{
    int Id { get; set; }
}

public interface IOrder : IEntity
{
    string Text { get; set; }
}

If we change the Create action to use an IOrder reference like so

[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Create(FormCollection form)
{
    IOrder order = new Order(); //This line has been changed
    UpdateModel(order, "order", form.ToValueProvider());

    ViewData["Id"] = order.Id;
    ViewData["Text"] = order.Text;

    return View();
}

we will se a somewhat surprising result:

result2 

What happens is that the posted value is not bound to the Id property of IOrder! The reason for this turns out to be that the Id property is declared in the IEntity interface. If you move the Id property from IEntity to IOrder, things will again be working as expected.

Tihs is something to keep in mind when you’re considering what to use for databinding in your application. If all your business objects are encapsulated in interfaces once they get to the web tier, you may have to provide special types for use in databinding, even though this to some extent defeats the purpose of databinding..

I have previously written a blog post about why this behaviour occurs: Hey, where are my interface’s properties?


How to determine if an ASP.NET OutputCacheModule is enabled for your application

No Comments »

I recently had an issue with an ASP.NET application concerning output caching. I thought output caching was configured correctly, but apparently pages weren’t being served from the cache. So, what do you do? Well, I decided to find out for real, whether the System.Web.Caching.OutputCacheModule was being run. When the OutputCacheModule is initialized it will register event handlers on the HttpApplication’s ResolveRequestCache and UpdateRequestCache events. Thus, I set out to determine who was subscribing to these events at runtime.

After spending some quality time with Reflector and the debugger I discovered that the HttpApplication uses a somewhat internal scheme to organize the registered event handlers. You can get to the datastructure containing the handlers through the HttpApplication.Events property of type EventHandlerList. However, in order to get to the desired delegate, you have to index this property with the correct key. In this case, the key is a private static readonly field of HttpApplication! Enter System.Reflection.

The key for getting at the ResolveRequestCache is called EventResolveRequestCache (likewise, for UpdateRequestCache it is called EventUpdateRequestCache). Hence, we may add the following code to Global.asax.cs to get to the subscribing HttpModules:

private IEnumerable GetResolveRequestCacheSubscribers()
{
    Type httpapptype = typeof(HttpApplication);
    FieldInfo f = httpapptype.GetField("EventResolveRequestCache",
                                                    BindingFlags.Static |
                                                    BindingFlags.NonPublic);
    object eventResulveRequestCacheKey = f.GetValue(this);
    Delegate dels = Events[eventResulveRequestCacheKey];
    if(dels == null)
        return Enumerable.Empty();
    else
        return (from d in dels.GetInvocationList()
                    where d.Target != null && d.Target is IHttpModule
                select d.Target as IHttpModule);
}

Enjoy


The behaviour of RedirectToAction(string action) changed in the ASP.NET MVC CTP 3

No Comments »

In the ASP.NET MVC CTP 2 the RedirectToAction(string action)-method will redirect to the action named on the same controller. However, this doesn’t happen in the new CTP 3. In this CTP the controller will not be set and the user will be redirected to the first route in the route table which matches the action name. Thus, you should change the call

RedirectToAction("Index")

in an action in MyController to

return RedirectToAction("Index", "MyController")

when migrating your code from CTP 2 to CTP 3. You could also create your own RedirectToCurrentControllerAction method on your controller, setting the controller explicitly:


public RedirectToRouteResult RedirectToCurrentController(string action)
{
RedirectToRouteResult red = RedirectToAction(action);
red.Values.Add("controller", this.ControllerContext.RouteData.Values["controller"]);
return red;
}

and in your action use

return RedirectToCurrentController("Index");