Make It Yours

Wednesday, February 2, 2011

I went to a general store, but they wouldn't let me buy anything specific. - Steven Wright.

One of the challenges of working with a general purpose programming language (like C#) and a general purpose framework (like ASP.NET, Silverlight, or any other application framework) is building something specific.

The trick is to apply constraints and build abstractions to take ownership of the situation, and there is an entire bag of fancy tricks to pull from, like using fluent APIs or applying domain driven design. But taking real ownership goes beyond just building the right classes - it's also about circumventing the underlying framework to match the needs of the team, the application, and the business in general. If you look at opinionated frameworks like OpenRasta, FubuMvc, and Caliburn.Micro, you'll find they make assumptions about what you want from a framework. These assumptions simplify design decisions and you'll often find it easier to find the one right way to achieve some goal.

Example, Please

Let's say your are using a relatively general purpose web framework like ASP.NET MVC 3. Every controller action needs to respond with one of two views depending on some bit of user information. To simply the scenario, we'll say we switch on the IsAuthenticated flag.

if (Request.IsAuthenticated)
{
    return View("private/index");
}
else
{
    return View("public/index");
}
image

Although it feels pretty good to keep the different views you need inside of separate folders, it's just a first step. The above code typifies what happens when you let the framework control you instead of taking control of the framework. When the above code appears inside of every primary controller action it reads like this: "the framework gives me a View method, and that's all I have to use."

To take this one step further you'll want to simplify the controller code and move the view decision out of the controller. Maybe this means you write a new helper method, a new action filter, or if the logic applies everywhere, a new view engine.

public class CustomViewEngine : IViewEngine
{
    ...

    public ViewEngineResult FindView(
        ControllerContext controllerContext, 
        string viewName, string masterName, bool useCache)
    {
        if (controllerContext.HttpContext.Request.IsAuthenticated)
        {
            viewName = "private/" + viewName;
        }
        else
        {
            viewName = "public/" + viewName;
        }
        return _inner.FindView(controllerContext, viewName, 
                               masterName, useCache);
    }

    RazorViewEngine _inner = new RazorViewEngine();
    ...
}

Then configure the engine into your application.

protected void Application_Start()
{
    ViewEngines.Engines.Clear();
    ViewEngines.Engines.Add(new CustomViewEngine());    
    ...
}

And now controller code is simple again.

return View();

The need for your application to offer two distinct views in every scenario is now implicitly baked into the infrastructure.

Conventions and Constraints

Taking control of the software often means you apply constraints (what I should not do) and conventions (what I don't need to do). The downside is having more implicit "magic" inside the code, but the upside is having simple code focused on specific solution.


Comments
gravatar jmorris Wednesday, February 2, 2011
Very good post. This technique also solves the DRY problem with putting that code to test the user's authentication in every controller.

Only problem I can think of is that if you do not break the content of the indexes into reusable partials, you then risk reintroducing duplication in each directory (private/public). IOW, you simply moved the problem to a different layer.
gravatar scott Wednesday, February 2, 2011
@jmorris - yes, very true.
Comments are now closed.
by K. Scott Allen K.Scott Allen
My Pluralsight Courses
The Podcast!