Pages

Monday, 18 January 2010

ASP.Net MVC's Hidden Web.Config File

I've just spent some time banging my head against a virtual brick wall built by my Build Server. It has, for some time now, been successfully building and deploying 2 versions (v1 and v2) of the same 'White Labelled' website to an internal testing web server. Recently I edited the definition so that it deleted all v2 files on the testing server before deploying. I did this because MVC Views that the developers had deleted, moved or renamed were remaining in the deployment and causing problems.

So, both versions were absolutely identical except for their web.config files that targeted different databases.

Both sites would allow me to hit the landing and login; but v2 failed to render any view that was strongly typed against a ViewModel. For instance, a view that inherited this would be fine...

System.Web.Mvc.ViewPage

... however, a view that inherited this, would fail...

System.Web.Mvc.ViewPage<MyApp.MyViewModel>

The exception was a System.Web.HttpParseException: "Could not load type 'MyApp.MyViewModel'".

Turns out that it was because I wasn't deploying that innocuous little web.config file that resides in the root View directory. One very important part of this file is to deny direct browsing to the aspx & ascx files - you want any requests to go via your routing model and controllers. The second purpose is to define the base types for your views and how to parse them.

Bottom line: without it your app won't work.

Friday, 15 January 2010

Google Analytics: Such a Shame.

I've been taking a look at Google Analytics for a couple of weeks and, quite frankly, how can you not be impressed? With the addition of a single block of javascript (2 scripts) GA will track all the activity on your site. Wow.

It captures hits, countries, browsers, referers and will show you a page overlay indicating where most of you visitors click to next. A web developer's dream "Look what I did, boss...".

However, here's the problem...

It's a script and they want me to put it on my site. That's fine for this blog, but I simply cannot use it on any commercial site that requires a user to login. Now, I'm not saying that GA would ever do anything untoward with that script but, technically, they could; and that's enough. If it felt so inclined, that script could access any DOM element on that page and even redirect POSTs to another endpoint. Login details, passwords and, in my case, financial data could all be collected and associated with the clients' IP address.

A crying shame - guess I'll just have to write my own server-side solution.

Saturday, 9 January 2010

Supercede Instance Pattern

This is a pattern introduced (to me at least) by Michael Feathers in his book "Working Effectively with Legacy Code". It deals with occasions where you have a Singleton pattern that it consumed by a large number of classes. This impedes these classes being Unit Tested as they all have a hidden dependency.

The first solution to this is to add a constructor overload to every class. You will also need to extract an interface for the Singleton. For instance...

public class MyClass
{
    public MyClass()
    {
    }

    public void DoWork()
    {
        MySingleton.Instance.DoWork();
    }
}

... would become ...

public class MyClass
{
    private IMySingleton _worker;

    public MyClass()
        : this (MySingleton.Instance)
    {
    }

    public MyClass(IMySingleton worker)
    {
        _worker = worker;
    }

    public void DoWork()
    {
        _worker.DoWork();
    }
}

You won't have to change any other code once the above it implemented. What this achieves is that your Unit Tests have the opportunity to use a Dependency Inversion pattern by using the non-default constructor. Simple, and nothing new.

However, this can be an awful lot of work - directly proportional to the number of classes that consume your singleton. This is where the Supercede Instance Pattern comes in.

This is probably a fair representation of the Singleton Pattern...

public class MySingleton : IMySingleton
{
    private MySingleton()
    {
    }

    private static IMySingleton _instance;

    public static IMySingleton Instance
    {
        get
        {
            // Double locking mechanism omitted for brevity...
            if (_instance == null)
                _instance = new MySingleton();
        }
    }
}

All we need to do is add a new method to the above class...

public class MySingleton : IMySingleton
{
    // All previous members omitted for brevity

    public static void SupercedeInstance(IMySingleton newInstance)
    {
        _instance = newInstance;
    }
}

Et voila. Your unit tests can call this new method and inject their own, chosen object into the Singleton. You've now broken the dependency of masses of classes in one hit. Nice.

Final point, if your language allows you could conditionally remove this method. That would keep your singleton 'safe' in the wild. For example...

public class MySingleton : IMySingleton
{
    #if RUNNING_UNIT_TESTS

    public static void SupercedeInstance(IMySingleton newInstance)
    {
        _instance = instance;
    }

    #endif
}

Thursday, 7 January 2010

ASP.Net MVC Groups


Here's a fictional example of a problem I've been having in ASP.Net MVC...
In an MVC application I have 1000 controllers. 2 of these controllers share a partial view so I put it in the ~/Views/Shared/ folder. I then find that I have 499 more pairs of controllers that also share different partial views so I do the same. The problem is that I now have a shared folder cluttered with 500 partial views - each of which is only relevant to 0.2% of my entire application! I also have to give each of these views unique names which becomes unwieldy.
So what's the solution?

What I'd like to do is group multiple controllers together and give them their own shared area for views. Any controller can also belong to more than one group.

Firstly, we need to extend the ViewEngine that our application is using. This is, primarily, responsible for finding the correct view in our filing system. Out of the box the framework uses  WebFormViewEngine. Our new view engine will inject extra, temporary locations in which to search for views. The information about which locations will be supplied by the current controller. Here's the code for the new engine...

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

namespace MyMvcApp.Web.Mvc
{
    ///
    /// GroupWebFormViewEngine extends WebFormViewEngine and allows Controllers to be
    /// decorated with the MvcGroupAttribute to indicate extra locations Views can be found.
    ///
    public class GroupWebFormViewEngine : WebFormViewEngine
    {
        private string[] _defaultMasterLocationFormats;
        private string[] _defaultPartialViewLocationFormats;
        private string[] _defaultViewLocationFormats;

        public GroupWebFormViewEngine()
            : base()
        {
        // We want to make a note of the base formats to that we can reset them prior to each Find operation
            _defaultMasterLocationFormats = MasterLocationFormats;
            _defaultPartialViewLocationFormats = PartialViewLocationFormats;
            _defaultViewLocationFormats = ViewLocationFormats;
        }

        public override ViewEngineResult FindView(ControllerContext controllerContext, string viewName, string masterName, bool useCache)
        {
            AddGroupLocations(controllerContext);

            return base.FindView(controllerContext, viewName, masterName, useCache);
        }

        public override ViewEngineResult FindPartialView(ControllerContext controllerContext, string partialViewName, bool useCache)
        {
            AddGroupLocations(controllerContext);

            return base.FindPartialView(controllerContext, partialViewName, useCache);
        }

        private void AddGroupLocation(ControllerContext controllerContext)
        {
            ResetAllLocations();

            var type = controllerContext.Controller.GetType();

            var attrs = type.GetCustomAttributes(typeof(MvcGroupAttribute), true);

            Array.ForEach(attrs, attr =>
            {
                AddGroupLocations((attr as MvcGroupAttribute).GroupName);
            });
        }

        private void ResetAllLocations()
        {
            MasterLocationFormats = _defaultMasterLocationFormats;
            PartialViewLocationFormats = _defaultPartialViewLocationsFormats;
            ViewLocationFormats = _defaultViewLocationFormats;
        }

        private void AddGroupLocations(string folderName)
        {
            AddMasterLocationFormats(folderName);
            AddPartialViewLocationFormats(folderName);
            AddViewLocationFormats();
        }

        private void AddMasterLocationFormats(string folderName)
        {
            List<string> currentLocations = new List<string>(MasterLocationFormats);

            currentLocations.Add("~/Views/Shared/" + folderName + "/{0}.master");

            MasterLocationFormats = currentLocations.ToArray();
        }

        private void AddPartialViewLocationFormats(string folderName)
        {
            List<string> currentLocations = new List<string>(PartialViewLocationFormats);

            currentLocations.Add("~/Views/Shared/" + folderName + "/{0}.aspx");
            currentLocations.Add("~/Views/Shared/" + folderName + "/{0}.ascx");

            PartialViewLocationFormats = currentLocations.ToArray();
        }

        private void AddViewLocationFormats()
        {
            ViewLocationFormats = PartialViewLocationFormats;
        }
    }
}

Now we just need our MvcGroupAttribute. This will be used to decorate our controller classes and should apply also to any inherited controllers. It is also possible to have more than one attribute on each controller as it might belong to multiple groups. Here's the code...

using System;

namespace MyMvcApp
{
    [AttributeUsage(AttributeTarget.Class, AllowMultiple = true, Inherited = true)]
    public class MvcGroupAttribute : Attribute
    {
        ///
        /// Gets or sets the name of a shared group that this controller belongs to.
        ///
        public string GroupName
        {
            get;
            set;
        }
    }
}

Now that we have all our building blocks we can finally use the functionality. Here's a trivial example...

using System;
using System.Web.Mvc;

namespace MyMvcApp
{
    [MvcGroup(GroupName = "Group1")]
    public class MyFirstController : Controller
    {
        public ActionResult Index()
        {
            return View();
        }
    }

    [MvcGroup(GroupName = "Group1")]
    public class MySecondController : Controller
    {
        public ActionResult Index()
        {
            return View();
        }
    }

    public class MyThirdController : Controller
    {
        public ActionResult Index()
        {
            return View();
        }
    }
}

In the example, above, all 3 controllers have access to views in the ~/Views/Shared/Group1/ directory.

Conclusion

This simple addition to our Mvc applications has been an enormous difference. We now have so much more flexibility to construct our views in a meaningful, clean way. Hopefully you'll find it useful too?