Web UI Migrations with ASP.NET MVC

Introduction

We recently faced a challenge in the project that I’ve been working on, to start working on the Web UI migration to a brand new Look and Feel UI. This sounds great, cause this is not just a minor tweak of visual elements… this is a fully blown rearchitecture of the Front End of our Single-Page Web Application.

The Challenge

Well, we all want the rearchitecture cause it will allow us to migrate/move from a mix of technologies into new (better planned for) frameworks (namely, we are moving from Knockout.js and Davis.js into AngularJS amongst others). However, we need to keep support and maintenance to the current UI Framework while we develop and test the new UI toolset.

Why is this a challenge, you ask? Well, while I would normally use a Source Control branching model for this new “Big Feature”, our current TFS Source Control in conjunction with some complex Continuous Integration environment makes it impossible to effectively use Branches.

So how do we achieve isolated development of the new UI in our ASP.NET MVC-based Web Application?

The Proposed Solution

Given the fact that we are only changing the Front End Framework set, the backend API services should remain (ideally) the same. So how about we find a way to put together the new UI while maintaining the old UI? I got the idea from Imran Baloch’s Blog… We create a Custom View Engine to dynamically render a View based on a UIVersion flag!!

I recommend you read this post “A Custom View Engine with Dynamic View Location” as it goes into details that I don’t really want to duplicate here (nor claim them my own original invention).

The Concepts

So we want to enable our set of Controllers, Models, ViewModels and Backend in general to be able to render different Views depending on the target UIVersion of our Web Application.

  • We need our Controllers to implement a UIVersion property that determines which Version of the Views the ViewEngine should render.
  • We need to make up a convention on how to name our Views so that we can create multiple Views for different Versions. For the sake of this post, we’ll use Index.v2.cshtml for UIVersion 2, and Index.cshtml as default.
  • We need to override our ViewEngine (RazorViewEngine in this post, but most of this can be analogously applied to the WebFormViewEngine) to select the right View to render depending on the UIVersion that the Controller determines.

The Code

Before we start, you can find a working copy of the code snippets listed here at Prosoft’s GitHub Blog Repository.

So, to achieve the first objective (to have our Controllers implement a UIVersion property), we simply need to create a base Controller class (if don’t already have one) and have all of our Controllers inherit from it (instead of the Controller class provided by the MVC Framework).

File /Controllers/BaseController.cs

///
/// Our Base Controller Class that we'll use as base of all of our Controllers.
/// We'll define common behavior here.
///
public abstract class BaseController : Controller
{
    public virtual int UIVersion
    {
        get { return 1; }
    }
}

Now that we have a base controller, and we have made all of our Controllers inherit from our BaseController, we’ll create a UIVersion 2 View file that we will represent our new UI to implement.

File /Views/Home/Index.v2.cshtml (we are assuming that a /Views/Home/Index.cshtml already exists)

@{
    Layout = null;
}
<!DOCTYPE html>
<html>
<head>
    <meta name="viewport" content="width=device-width" />
    <title>Version 2 of the Home page</title>
</head>
<body>
    <div>
        This is the Version 2 of the home page.
    </div>
</body>
</html>

And the 3rd and final objective, we need to create a Custom ViewEngine that will understand the UIVersion property (when available) and render the correct View for it. For this, we follow the steps in this post, and our final MyRazorViewEngine will look something like this:

File /ViewEngines/UIVersionedRazorViewEngine.cs

    public class UIVersionedRazorViewEngine : RazorViewEngine
    {
        public UIVersionedRazorViewEngine()
            : base()
        {
            AreaViewLocationFormats = new[] {
                "~/Areas/{2}/Views/{1}/{0}.v%UIVersion%.cshtml",
                "~/Areas/{2}/Views/{1}/{0}.v%UIVersion%.vbhtml",
                "~/Areas/{2}/Views/Shared/{0}.v%UIVersion%.cshtml",
                "~/Areas/{2}/Views/Shared/{0}.v%UIVersion%.vbhtml",
                "~/Areas/{2}/Views/{1}/{0}.cshtml",
                "~/Areas/{2}/Views/{1}/{0}.vbhtml",
                "~/Areas/{2}/Views/Shared/{0}.cshtml",
                "~/Areas/{2}/Views/Shared/{0}.vbhtml"
            };

            AreaMasterLocationFormats = new[] {
                "~/Areas/{2}/Views/{1}/{0}.v%UIVersion%.cshtml",
                "~/Areas/{2}/Views/{1}/{0}.v%UIVersion%.vbhtml",
                "~/Areas/{2}/Views/Shared/{0}.v%UIVersion%.cshtml",
                "~/Areas/{2}/Views/Shared/{0}.v%UIVersion%.vbhtml",
                "~/Areas/{2}/Views/{1}/{0}.cshtml",
                "~/Areas/{2}/Views/{1}/{0}.vbhtml",
                "~/Areas/{2}/Views/Shared/{0}.cshtml",
                "~/Areas/{2}/Views/Shared/{0}.vbhtml"
            };

            AreaPartialViewLocationFormats = new[] {
                "~/Areas/{2}/Views/{1}/{0}.v%UIVersion%.cshtml",
                "~/Areas/{2}/Views/{1}/{0}.v%UIVersion%.vbhtml",
                "~/Areas/{2}/Views/Shared/{0}.v%UIVersion%.cshtml",
                "~/Areas/{2}/Views/Shared/{0}.v%UIVersion%.vbhtml",
                "~/Areas/{2}/Views/{1}/{0}.cshtml",
                "~/Areas/{2}/Views/{1}/{0}.vbhtml",
                "~/Areas/{2}/Views/Shared/{0}.cshtml",
                "~/Areas/{2}/Views/Shared/{0}.vbhtml"
            };

            ViewLocationFormats = new[] {
                "~/Views/{1}/{0}.v%UIVersion%.cshtml",
                "~/Views/{1}/{0}.v%UIVersion%.vbhtml",
                "~/Views/Shared/{0}.v%UIVersion%.cshtml",
                "~/Views/Shared/{0}.v%UIVersion%.vbhtml",
                "~/Views/{1}/{0}.cshtml",
                "~/Views/{1}/{0}.vbhtml",
                "~/Views/Shared/{0}.cshtml",
                "~/Views/Shared/{0}.vbhtml"
            };

            MasterLocationFormats = new[] {
                "~/Views/{1}/{0}.v%UIVersion%.cshtml",
                "~/Views/{1}/{0}.v%UIVersion%.vbhtml",
                "~/Views/Shared/{0}.v%UIVersion%.cshtml",
                "~/Views/Shared/{0}.v%UIVersion%.vbhtml",
                "~/Views/{1}/{0}.cshtml",
                "~/Views/{1}/{0}.vbhtml",
                "~/Views/Shared/{0}.cshtml",
                "~/Views/Shared/{0}.vbhtml"
            };

            PartialViewLocationFormats = new[] {
                "~/Views/{1}/{0}.v%UIVersion%.cshtml",
                "~/Views/{1}/{0}.v%UIVersion%.vbhtml",
                "~/Views/Shared/{0}.v%UIVersion%.cshtml",
                "~/Views/Shared/{0}.v%UIVersion%.vbhtml",
                "~/Views/{1}/{0}.cshtml",
                "~/Views/{1}/{0}.vbhtml",
                "~/Views/Shared/{0}.cshtml",
                "~/Views/Shared/{0}.vbhtml"
            };
        }

        protected override IView CreatePartialView(ControllerContext controllerContext, string partialPath)
        {
            return base.CreatePartialView(controllerContext, ContextualizePath(controllerContext, partialPath));
        }

        protected override IView CreateView(ControllerContext controllerContext, string viewPath, string masterPath)
        {
            var nameSpace = controllerContext.Controller.GetType().Namespace;
            return base.CreateView(controllerContext, ContextualizePath(controllerContext, viewPath), ContextualizePath(controllerContext, masterPath));
        }

        protected override bool FileExists(ControllerContext controllerContext, string virtualPath)
        {
            var nameSpace = controllerContext.Controller.GetType().Namespace;
            return base.FileExists(controllerContext, ContextualizePath(controllerContext, virtualPath));
        }

        protected string ContextualizePath(ControllerContext controllerContext, string path)
        {
            var UIVersion = 1;
            if (controllerContext.Controller is BaseController)
            {
                UIVersion = (controllerContext.Controller as BaseController).UIVersion;
            }
            return path.Replace("%UIVersion%", UIVersion.ToString());
        }
    }

Make sure you register your customized ViewEngine with the MVC Framework as explained in this post.

We are almost done now. The only pending step is to setup our Controller to use whatever logic you want to determine the UIVersion. In this example, I’ve overriden the UIVersion property (though I could’ve just modified the BaseController) to get the UI Version from the Query String parameter “UIVersion”.

File /Controllers/HomeController.cs

    public class HomeController : BaseController
    {
        public override int UIVersion
        {
            get 
            {
                var UIVersion = 1;
                Int32.TryParse(this.ControllerContext.HttpContext.Request.QueryString["UIVersion"], out UIVersion);
                return UIVersion;
            }
        }

        public ActionResult Index()
        {
            return View();
        }

    }

Testing the Code

Our solution is ready to be put to the test. All we need to do is navigate to the Home path ~/ in our browser. Then, navigate to ~/?UIVersion=2. You should see the difference.

Multi-Versioned UI

Conclusion

Hope this works out useful for you. Really, this approach just unveils the power of the ASP.NET MVC Framework to extend its functionality and achieve whatever you want! Keep in mind that you can use this ViewEngine feature overriding for parallel UI development, or even to allow users to experience different View Look & Feel based on whatever parameter you prefer (maybe you have to show a different UI experience to different clients on the same UI).

Anyway, be sure to leave me feedback below if you liked this post (and even if you didn’t, but with constructive criticism in mind ;)).

New Office Construction Update

The new office construction in Costa Rica is taking shape as the electrical wiring has been completed, drywall has been hung, fire prevention is in-place and the air conditioning units are installed.  We’re really looking forward to having the new space as a more comfortable collaboration space for teams to work with their U.S.-based teammates.  Latitud Norte is a brand new building located in Escazú, very near the new Sheraton Hotel and Casino.  It has excellent amenities, including an outdoor covered dining area (with a tremendous view) covered parking and a workout facility with showers.  For our employees that come to the office daily, it will be a major upgrade from our facility in Heredia (which was also nice, but lacked all of those things).  The building also has full generator backup for the occasional power outage and access to much higher internet speeds than our location in Heredia.  The building is very secure with 24/7 security and swipe-card access to the building and our offices.  The new facility will really bring us to a new level of service and collaboration.  We are looking forward to welcoming visitors around the second week of December.

[auto_column columns=”2″][span6]prosoft01[/span6]
[span6]prosoft02[/span6]
[/auto_column]

 

Effective tools and tips for successful software developers teams

Effective Tools and Tips
By David Easterling

I met with a client yesterday that has a team of four software developers with us.  We have been working with them for three years, so everyone is very familiar and we contribute quite a bit in design phase.  They use Skype for standups and chat and some screen share.  We were talking about the productivity level of our guys and he said “It would be nearly 100% of our local team if I could walk over and show them something on the whiteboard rather than write it up and email it.”  I know there are some screensharing applications like Lync and Skype.   I found one recently called Screenhero that is really good for collaborative screensharing.  It has a great whiteboard application and is free to use.   I believe strongly in maximizing your ability to leverage off-site development staff.  Little tools like this can really help.  Another client was telling me that they don’t have video standups with their off-site people – just phone calls.  If you’re only doing voice, there’s a lot you’re not seeing.  You may notice that someone is checked out or typing or that they really check out when one team member is talking.  They may also show with their body language that they don’t agree with a particular direction you’re taking, but don’t speak up.  You can either address this off-line or in the meeting, but you would never know in a voice call.  Most importantly, if everyone in the standup is at their desk on video, they’re all equal and equally informed.  Little things like that make for a much more productive off-site development experience.

Costa Rica: One of the top remote collaboration technology leaders

Team Organization and CollaborationBy David Easterling

I was meeting a client for lunch yesterday and he asked he should invite David to join us.  I asked “David who?”  He reminded me that my team lead for that account, who is based in Costa Rica, was in the office this week.  I had forgotten.  David is sort of a world traveler, so it’s hard to nail down exactly where he is at any given moment.  His girlfriend has been living in Kuala Lumpur for the past year, so occasionally he will work from there for a month or so.  He also has a sister in Germany, so he spent a few weeks there last year as well.  David is originally from Honduras, but recently emigrated to Costa Rica, so we have all decided it’s sort of irrelevant where he is and way too hard to track.  The one place we always know he’ll be is on Skype and Lync every morning at 8:00 AM PT and in the standup at 9:30.  We always know where his code will be too:  checked in nightly in TFS.  Our clients love the attractive relative proximity of Costa Rica, but it’s working on the same code base at the same time that has the real value — and that can be done from virtually anywhere.  As a result Prosoft employees are prone to roam the earth from time to time.  Orlando is on an extended visit to Brazil.  Esteban regularly comes to the U.S. to DJ huge raves.  Lenin spends a fair amount of time in Pennsylvania.  Each of these guys works a full schedule while they are traveling and the client doesn’t miss a beat.  We’re really entering a new age of remote collaboration.  It’s fun to be a part of it and we’ve only scratched the surface.