How to Localized MVC Routing with Area Without Specifying Culture Short Name in the Url

In a previous post, I wrote how to enhance the standard Asp.Net MVC Routing to have localized URL with controller and action. This solution had some flaws that this article will cover. First of all, it was not working with area. Second, you were not able to use all functionalities like constraints and default values and third, it was a lot of configuration by having to use a lot of lists.

This article illustrates an easier way to define your routing configuration by adding a Fluent API but also by giving you all standard routing capabilities. This solution will let you configure every route possibles : values, defaults values, constraints, etc and will enhance the standard MVC routing by figuring out which language to display all your resources by setting the culture for you.

Before going any further, here is an example of what the system let you use without Fluent API.

var controllerRoutes = new List<ControllerSectionLocalized>{
    new ControllerSectionLocalized("Home"
            , new List<LocalizedSection>{
                        new LocalizedSection(LocalizedSection.EN, "Home")
                    ,new LocalizedSection(LocalizedSection.FR, "Demarrer")
            }
        ,new List<ActionSectionLocalized>{
                new ActionSectionLocalized("Index"
                , new List<LocalizedSection>{
                        new LocalizedSection(LocalizedSection.EN, "Index")
                    ,new LocalizedSection(LocalizedSection.FR, "Index")
                }
                , new { id = ""}
                , null
                , "{controller}/{action}/{id}"
                )

        })
    ,
    new ControllerSectionLocalized("Account"
            , new List<LocalizedSection>{
                        new LocalizedSection(LocalizedSection.EN, "Account")
                    ,new LocalizedSection(LocalizedSection.FR, "Compte")
            }
        ,new List<ActionSectionLocalized>{
                new ActionSectionLocalized("Profile"
                , new List<LocalizedSection>{
                        new LocalizedSection(LocalizedSection.EN, "Profile")
                    ,new LocalizedSection(LocalizedSection.FR, "Profile")
                }
                , new {username = UrlParameter.Optional }
                , null
                , "{action}/{username}"
                )
            , new ActionSectionLocalized("DisplayBadges"
                , new List<LocalizedSection>{
                        new LocalizedSection(LocalizedSection.EN, "Badges")
                    ,new LocalizedSection(LocalizedSection.FR, "Medailles")
                }
                , null
                , null
                , "{action}"
                )
            , new ActionSectionLocalized("Privilege"
                , new List<LocalizedSection>{
                        new LocalizedSection(LocalizedSection.EN, "Privilege-benefits")
                    ,new LocalizedSection(LocalizedSection.FR, "benefice-des-privileges")
                }
                , null , null
                , "{action}"
                )
            , new ActionSectionLocalized("PrivilegeBuyConfirm"
                , new List<LocalizedSection>{
                        new LocalizedSection(LocalizedSection.EN, "Privilege-buy-confirm")
                    ,new LocalizedSection(LocalizedSection.FR, "confirmation-achat-privilege")
                }
                , null , null
                , "{action}"
                )
            , new ActionSectionLocalized("Login"
                , new List<LocalizedSection>{
                        new LocalizedSection(LocalizedSection.EN, "Login")
                    ,new LocalizedSection(LocalizedSection.FR, "Identification")
                }
                , null
                , null
                , "{controller}/{action}"
                )
            , new ActionSectionLocalized("ReSendingCreationEmail"
                , new List<LocalizedSection>{
                        new LocalizedSection(LocalizedSection.EN, "SendValidationMail")
                    ,new LocalizedSection(LocalizedSection.FR, "EnvoieCourrielDeValidation")
                }
                , null , null
                , "{controller}/{action}/{emailAddress}/now"/* We need to be able to have more than just action translated but also text token for e.g. NOW*/
                )
            , new ActionSectionLocalized("ActivateAccount"
                , new List<LocalizedSection>{
                        new LocalizedSection(LocalizedSection.EN, "ActivateAccount")
                    ,new LocalizedSection(LocalizedSection.FR, "activer-compte")
                }
                , null , null
                , "{controller}/{action}/{emailAddress}/now"/* We need to be able to have more than just action translated but also text token for e.g. NOW*/
                )
        }
        )
};

And with the Fluent Routing API:

            var controllerRoutes = FluentLocalizedRoute.BuildRoute()
                                                        .ForBilingualController("Home", "Home", "Demarrer")
                                                            .WithBilingualAction("Index", "Index", "Index")
                                                                .WithDefaultValues(new { id = "" })
                                                                .UseDefaulUrl()
                                                        .ForBilingualController("Account", "Account", "Compte")
                                                             .WithBilingualAction("Profile", "Profile", "Profile")
                                                                .WithDefaultValues(new { username = UrlParameter.Optional })
                                                                .WithUrl("{action}/{username}")
                                                             .And().WithBilingualAction("DisplayBadges", "Badges", "Medailles")
                                                                   .WithUrl("{action}")
                                                             .And().WithBilingualAction("Privilege", "Privilege-benefits", "benefice-des-privileges")
                                                                   .WithUrl("{action}")
                                                             .And().WithBilingualAction("PrivilegeBuyConfirm", "Privilege-buy-confirm", "confirmation-achat-privilege")
                                                                   .WithUrl("{action}")
                                                             .And().WithBilingualAction("Login", "Login", "Identification")
                                                                   .WithUrl("{controller}/{action}")
                                                             .And().WithBilingualAction("ReSendingCreationEmail", "SendValidationMail", "EnvoieCourrielDeValidation")
                                                                   .WithUrl("{controller}/{action}/{emailAddress}/now")
                                                             .And().WithBilingualAction("ActivateAccount", "ActivateAccount", "activer-compte")
                                                                   .WithUrl("{controller}/{action}/{emailAddress}/now")
                                                                   .ToList()
                                                             ;

As you can see, it’s way more concise. Of course, the method used in the API focus on 2 languages but underneath you can have unlimited localization — it’s just more convenient for a lot of people to have a bilingual application, thus these helper methods. So what does this localized code will give you:

  • Url that can be in an unlimited language bound to Mvc code
  • Url that change the Culture and CultureUI without having to specify the local like en-us or fr-ca
  • A system that handles Area, Controller and Action to be translated
  • A system that generate subsequent URL with the default Asp.Net MVC Helper in the right language
  • A system compatible with the current Asp.Net routing system, thus both can be run in parallel
  • A Fluent API that let you write quickly routing
  • Full support of current Asp.Net feature with default values, constraints and even with custom language token that can be localized within the URL

Let’s start without having the Fluent API because the Fluent API it’s just something you add above the custom multilanguage Asp.Net Mvc routing system we are developing.

The first class that we need is the one that will hold the localized string.

public class LocalizedSection
{
	public static CultureInfo EN = CultureInfo.GetCultureInfo("en-US");
	public static CultureInfo FR = CultureInfo.GetCultureInfo("fr-FR");
	public CultureInfo CultureInfo { get; set; }
	public string TranslatedValue { get; set; }
	public LocalizedSection(CultureInfo culture, string translatedValue)
	{
		CultureInfo = culture;
		TranslatedValue = translatedValue;
	}
}

This LocalizedSection class is pretty simple by having a single constructor that take a culture and a string that is localized. Some static properties are there because I am developing an English-French system and wanted to have the culture that I will use defined once. Even if everything explained in this article is about a bilingual system in English and French, the system is already in shape to let you use other languages than these two but also more than two. Once you have that class, you need to defines the structure of how we will keep all localized sections for the system. The structure is the same as Asp.Net MVC routing which is area, controller and action. We will build everything to support Area-Controller-Action and Controller-Action. To do so, we need three classes.

    public class AreaSectionLocalized
    {
        public string AreaName { get; set; }
        public List<LocalizedSection> Translation { get; set; }
        public List<ControllerSectionLocalized> ControllerTranslations { get; set; }

        public AreaSectionLocalized(string areaName, List<LocalizedSection> translation, List<ControllerSectionLocalized> controllersList)
        {
            this.AreaName = areaName;
            this.Translation = translation;
            this.ControllerTranslations = controllersList;
        }
    }
	
    public class ControllerSectionLocalized
    {
        public string ControllerName { get; set; }
        public List<LocalizedSection> Translation { get; set; }
        public List<ActionSectionLocalized> ActionTranslations { get; set; }

        public ControllerSectionLocalized(string controllerName, List<LocalizedSection> translation, List<ActionSectionLocalized> actionsList)
        {
            this.ControllerName = controllerName;
            this.Translation = translation;
            this.ActionTranslations = actionsList;
        }
    }
	
    public class ActionSectionLocalized
    {
        public string ActionName { get; set; }
        public List<LocalizedSection> Translation { get; set; }

        public object Values { get; set; }
        public object Constraints { get; set; }
        public string Url { get; set; }
        public ActionSectionLocalized(string actionName, List<LocalizedSection> translation, object values = null, object constraints = null, string url = "")
        {
            this.ActionName = actionName;
            this.Translation = translation;
            this.Values = values;
            this.Constraints = constraints;
            this.Url = url;
        }
        public Dictionary<string,List<LocalizedSection>> Tokens { get; set; }
    }

The AreaSectionLocalized class is the one that contain the real area’s name under the property named AreaName. This one will be used to map when you request from Mvc Html Helper a route with the real name but also when you specify in the url a localized string from the Translation property. This list, Translation , is where you store the collection of localized area name for this area. For example, if you have an area in your code called “Order” than you can have the area to be displayed in the url has “customer-area” and “commande-du-client”. Since it’s a list, you can have unlimited translation — each of them are linked to a CultureInfo. The last important property of that class is the collection of controller. As you can see, we are building a three of routing. At the root will be a collection of area, with AreaSectionLocalized. Each of area will have a list of controller, which will have a list of action. That is why the ControllerSectionLocalized look alike the area class. The action class, ActionSectionLocalized is the class among the three that contains more information. It has also the same principle of having the action name which is the one that the controller class define and use in Asp.Net MVC Url Helper with a list of localized string but also multiple properties. Values, constraints, Url are all information used to build the URL in the standard Asp.Net MVC routing system. Here is a standard route :

routes.MapRoute(Constants.Areas.CONTEST + "_contest_detail"
	, Constants.Areas.CONTEST + "/{" + Constants.RoutePortionName.ACTIVE_CURRENT_CONTEST_ID + "}/Detail"
	, new RouteValueDictionary { { Constants.RoutePortionName.ACTION, "Detail" }, { Constants.RoutePortionName.CONTROLLER, "UserContest" }}
	, new RouteValueDictionary {{ Constants.RoutePortionName.ACTIVE_CURRENT_CONTEST_ID,@"\d+"}
});

The second line is the URL, the third line is the value and the forth line is the constraint. We also do the same in the ActionSectionLocalized class. One custom new principle is the Tokens list. This is a dictionary of string that we will replace with a localized string. Item of the list represent a token, every value contains a list of localized text to replace in the URL. That mean that you can create an URL that look like this : “/{area}/{controller}/{action}/always/{page}/{pagenumber} where the area, controller and action will be replaced with the according value as the standard Asp.Net MVC routing is normally doing, the same is true about the hard-coded “always” that will remains the same whatever the language and the {pagenumber} if defined in the URL will be used as a routing variable. The difference is that if we detect that {page} is not used as a routing value that we will lookup in the token list to see if it could be replaced with a localized value. We will see it in more detail later.

The next one is one of the code class of the system, it’s the new route class. This localized route class, named accordingly to its goal LocalizedClass, inherit from the Asp.Net MVC’s Route class. This way, it’s possible to continue to use the same routing system without having to re-invent the wheel.

public class LocalizedRoute : Route
{
    public CultureInfo Culture { get; private set; }
    public ActionSectionLocalized ActionTranslation { get; private set; }
    public ControllerSectionLocalized ControllerTranslation { get; private set; }
    public AreaSectionLocalized AreaSectionLocalized { get; private set; }

    public LocalizedRoute(AreaSectionLocalized areaSectionLocalized, ControllerSectionLocalized controllerTranslation, ActionSectionLocalized actionTranslation, string url
                        , RouteValueDictionary defaults, RouteValueDictionary constraints, CultureInfo culture)
        : this(areaSectionLocalized, controllerTranslation, actionTranslation, url, defaults, constraints, null, new MvcRouteHandler(), culture)
    {

    }

    public LocalizedRoute(AreaSectionLocalized areaSectionLocalized, ControllerSectionLocalized controllerTranslation, ActionSectionLocalized actionTranslation, string url
                        , RouteValueDictionary defaults, RouteValueDictionary constraints, RouteValueDictionary dataTokens, IRouteHandler routeHandler, CultureInfo culture)
        : base(url, defaults, constraints, dataTokens, routeHandler)
    {
        this.AreaSectionLocalized = areaSectionLocalized;


        if (controllerTranslation == null)
        {
            throw new ArgumentNullException("controllerTranslation");
        }
        this.ControllerTranslation = controllerTranslation;
           
        if (actionTranslation == null)
        {
            throw new ArgumentNullException("actionTranslation");
        }
        this.ActionTranslation = actionTranslation;

        if (url == null)
        {
            throw new ArgumentNullException("url");
        }

        if (culture == null)
        {
            throw new ArgumentNullException("culture");
        }
        this.Culture = culture;

        if (dataTokens == null)
        {
            base.DataTokens = new RouteValueDictionary();
        }

        if (base.Defaults != null && base.Defaults.Keys.Contains(Constants.AREA))
        {
            if (base.DataTokens == null)
            {
                base.DataTokens = new RouteValueDictionary();
            }
            base.DataTokens.Add(Constants.AREA, base.Defaults[Constants.AREA].ToString());
        }
    }

    /// <summary>
    /// Set the thread culture with the route culture
    /// </summary>
    /// <param name="httpContext"></param>
    /// <returns></returns>
    public override RouteData GetRouteData(HttpContextBase httpContext)
    {
        var returnRouteData =  base.GetRouteData(httpContext);
        if (returnRouteData != null)
        {
            System.Threading.Thread.CurrentThread.CurrentCulture = this.Culture;
            System.Threading.Thread.CurrentThread.CurrentUICulture = this.Culture;
        }
        return returnRouteData;
    }
    protected override bool ProcessConstraint(HttpContextBase httpContext, object constraint, string parameterName, RouteValueDictionary values, RouteDirection routeDirection)
    {
        return base.ProcessConstraint(httpContext, constraint, parameterName, values, routeDirection);
    }

    public override VirtualPathData GetVirtualPath(RequestContext requestContext, RouteValueDictionary values)
    {
        var currentThreadCulture = System.Threading.Thread.CurrentThread.CurrentUICulture;
        //First step is to avoid route in the wrong culture
        if (this.Culture.Name != currentThreadCulture.Name)
        {
            return null;
        }

        //Second, set the right Area/Controller/Action to have MVC generating the URL with the localized string
        bool replaceRoutingValues = true;
        LocalizedSection areaTranslated = null;
        LocalizedSection controllerTranslated = null;
        LocalizedSection actionTranslated = null;
        if (this.AreaSectionLocalized != null && values[Constants.AREA] !=null) //If added in the RouteValue, it will be just there later during GetVirtualPath (merge from MVC's route creation code)
        {
            var valueToken = values[Constants.AREA];
            areaTranslated = this.AreaSectionLocalized.Translation.FirstOrDefault(d => d.CultureInfo.Name == currentThreadCulture.Name);
            replaceRoutingValues = (areaTranslated != null && areaTranslated.TranslatedValue == valueToken);
        }

        if (replaceRoutingValues && this.ControllerTranslation != null)
        {
            var valueToken = values[Constants.CONTROLLER];
            controllerTranslated = this.ControllerTranslation.Translation.FirstOrDefault(d => d.CultureInfo.Name == currentThreadCulture.Name);
            replaceRoutingValues &= (controllerTranslated != null && controllerTranslated.TranslatedValue == valueToken);
        }

        if (replaceRoutingValues && this.ActionTranslation != null)
        {
            var valueToken = values[Constants.ACTION];
            actionTranslated = this.ActionTranslation.Translation.FirstOrDefault(d => d.CultureInfo.Name == currentThreadCulture.Name);
            replaceRoutingValues &= (actionTranslated != null && actionTranslated.TranslatedValue == valueToken);
        }

        //We need to find a translation that fit at least Controller and Action
        //if (!replaceRoutingValues)
        //{
        //    return null;
        //}

        //Switch text token to the right language
        if (this.ActionTranslation != null)
        {
            base.Url = ReplaceTokens(base.Url, this.ActionTranslation.Tokens);
        }

        // Check with the new values if the system can get an URL with the values in the culture desired
        var vitualPathData = this.GetVirtualPathForLocalizedRoute(requestContext, values);
        //vitualPathData.DataTokens
        // Asp.Net MVC found a URL, time to enhance the URL with localization replacement
        if (vitualPathData != null)
        {
            //This is to replace {action}, {controller} and {area} with the localized version
            vitualPathData.VirtualPath = LocalizedSection.ReplaceSection(this.Url, areaTranslated, controllerTranslated, actionTranslated);
            //Enhance url with replace or append route value dictionary 
            vitualPathData.VirtualPath = AdjustVirtualPathWithRoutes(vitualPathData.VirtualPath, values);
            //Default value if not defined in the route value
            vitualPathData.VirtualPath = AdjustVirtualPathWithActionTranslationDefaultValues(vitualPathData.VirtualPath, values);
            vitualPathData.VirtualPath = vitualPathData.VirtualPath.TrimEnd('/');
        }
        return vitualPathData;
    }

    /// <summary>
    /// Adjust virtual path with action translation default value not in the route. This is because we can define default and the
    /// value of default is only used when not more specific from the route.
    /// 
    /// Route has precedence on Default Value (this.ActionTranslation.Values)
    /// </summary>
    /// <param name="currentVirtualPath"></param>
    /// <param name="values"></param>
    /// <returns></returns>
    public string AdjustVirtualPathWithActionTranslationDefaultValues(string currentVirtualPath, RouteValueDictionary values)
    {
        if (string.IsNullOrEmpty(currentVirtualPath) || values == null)
        {
            return currentVirtualPath;
        }
        string finalVirtualPath = currentVirtualPath;
        //This is for the case that optional parameter in the action are not defined in the URL
        if (this.ActionTranslation != null)
        {
            var rc = new RouteValueDictionary(this.ActionTranslation.Values);
            // If defined {word} is not in the URL, then we use the value from the actionTranslated
            foreach (var key in rc.Keys.Where(q => !values.ContainsKey(q)))
            {
                string toReplace = "{" + key + "}";
                finalVirtualPath = finalVirtualPath.Replace(toReplace, System.Net.WebUtility.UrlEncode(rc[key].ToString()));
            }
        }
        return finalVirtualPath;
    }

    /// <summary>
    /// Get all routes information that are not Area-Controller-Action and change the value from the URL.
    /// If not in the URL, add the data in query string
    /// </summary>
    /// <param name="currentVirtualPath"></param>
    /// <param name="values"></param>
    /// <returns></returns>
    public string AdjustVirtualPathWithRoutes(string currentVirtualPath, RouteValueDictionary values)
    {
        string finalVirtualPath = currentVirtualPath;
        if (values != null)
        {
            foreach (var key in values.Keys.Where(k => k != Constants.AREA && k != Constants.CONTROLLER && k != Constants.ACTION))
            {
                string toReplace = "{" + key + "}";
                if (values[key] != null)
                {
                    string replaceWith = System.Net.WebUtility.UrlEncode(values[key].ToString());
                    if (currentVirtualPath.Contains(toReplace))
                    {
                        finalVirtualPath = finalVirtualPath.Replace(toReplace, replaceWith);
                    }
                    else
                    {
                        finalVirtualPath = AddKeyValueToUrlAsQueryString(finalVirtualPath, toReplace, replaceWith);
                    }
                }
            }
        }

        return finalVirtualPath;
    }

    public string ReplaceTokens(string url, Dictionary<string, List<LocalizedSection>> tokens)
    {
        if (tokens!= null)
        {
            foreach (var key in tokens.Keys)
            {
                var tokenInCurrentCulture = tokens[key].FirstOrDefault(f => f.CultureInfo.Name == this.Culture.Name);
                if (tokenInCurrentCulture != null)
                {
                    string toReplace = "{" + key + "}";
                    return url.Replace(toReplace, System.Net.WebUtility.UrlEncode(tokenInCurrentCulture.TranslatedValue));
                }
            }
        }
        return url;
    }

    public string AddKeyValueToUrlAsQueryString(string url, string key, string value)
    {
        if (!string.IsNullOrEmpty(key) && url!=null)
        {
            key = key.Replace("{", "").Replace("}", "");
            if (url.Contains("?"))
            {
                return url + "&" + key + "=" + value;
            }
            else
            {
                return url + "?" + key + "=" + value;
            }
        }
        return url;
    }

    public virtual VirtualPathData GetVirtualPathForLocalizedRoute(RequestContext requestContext, RouteValueDictionary values)
    {
        return base.GetVirtualPath(requestContext, values);
    }
}

I will not describe everything since I added comment directly inside the code but the gist of the class is to manipulate the URL and routing by overriding GetVirtualPath to be sure to convert the localized route section requested into the original Area-Controller-Action name, to use the default code to get url that would be generated by ASP.Net MVC and then enhance this one by localizing this one with Area-Controller-Action. You can see the GetVirtualPath as the entry point that is called for every route defined when you use any ASP.Net MVC mechanism to get an URL. The framework loops all the route by calling GetVirtualPath and if one return not NULL, it takes this one.

This class also override GetRouteData which is used when the user enter an URL in a browser. That time, since we are still using the default route mechanism and that we store the route with the standard Area-Controller-Action + Constraint that we have almost nothing to do. We only set the current thread Culture from the culture defined by the route.

The last remaining piece to be able to have everything work is to create an helper to add localized route to the ASP.Net MVC’s RouteCollection. I decided to create a static method that take the route and a list of AreaSectionLocalized or ControllerSectionLocalized.

public static class RouteCollectionExtension
{

    public static void AddRoutes(this RouteCollection routes, List<AreaSectionLocalized> areaRoutes)
    {
        foreach (var area in areaRoutes)
        {
            routes.AddRoutes(area.ControllerTranslations, area);
        }
    }

    public static void AddRoutes(this RouteCollection routes, List<ControllerSectionLocalized> controllerRoutes, AreaSectionLocalized areaSectionLocalized = null)
    {
        foreach (var controller in controllerRoutes)
        {
            foreach (var controllerTranslation in controller.Translation)
            {
                foreach (var action in controller.ActionTranslations)
                {
                    var urlAction = action.Url;

                    foreach (var actionTranslation in action.Translation)
                    {
                        if (controllerTranslation.CultureInfo == actionTranslation.CultureInfo)
                        {
                            RouteValueDictionary values = null;
                            if (action.Values is RouteValueDictionary)
                            {
                                values = action.Values as RouteValueDictionary;
                            }
                            else
                            {
                                values = new RouteValueDictionary(action.Values);
                            }
                            LocalizedSection areaTranslation = null;
                            if (areaSectionLocalized != null && areaSectionLocalized.Translation.Any(d => d.CultureInfo.Name == controllerTranslation.CultureInfo.Name))
                            {
                                values[Constants.AREA] = areaSectionLocalized.AreaName;
                                areaTranslation = areaSectionLocalized.Translation.FirstOrDefault(d => d.CultureInfo.Name == controllerTranslation.CultureInfo.Name);
                            }
                            values[Constants.CONTROLLER] = controller.ControllerName;
                            values[Constants.ACTION] = action.ActionName;
                            RouteValueDictionary constraints = null;
                            if (action.Constraints is RouteValueDictionary)
                            {
                                constraints = action.Constraints as RouteValueDictionary;
                            }
                            else
                            {
                                constraints = new RouteValueDictionary(action.Constraints);
                            }
                            var newUrl = LocalizedSection.ReplaceSection(urlAction, areaTranslation, controllerTranslation, actionTranslation);
                            routes.Add(new LocalizedRoute(
                                    areaSectionLocalized
                                , controller
                                , action
                                , newUrl
                                , values
                                , constraints
                                , actionTranslation.CultureInfo
                                )
                            );
                        }
                    }
                }
            }
        }
    }
}

The code is adding a route by looping through all areas, all controllers, all actions and for each language add the route. I will create a second article to describe to Fluent interface that help to have a more concise way to write the routing and also that gives Microsoft Intellisence support. So far, in this article, we have seen how to enhance the existing Asp.Net MVC routing system by having localized route. The code handles the thread culture, thus by changing the URL you have all your pages in the right local too. Finally, we saw that

Dapper.Net coexistence with Entity Framework and caveats

Dapper.Net is a micro ORM (objection relational mapping) that StackOverFlow is using. It’s open source and it still maintained by the team, mostly by Marc Gravell who is a top 5 users in StackOverflow too. The project I am working on is getting slowed down by Entity Framework (EF) since few months and the introduction to an alternative solution was required. This is why I introduced Dapper.Net in the solution. The goal is to slowly switch toward something more under my control and Dapper.Net offers this by letting you write queries in SQL directly. In theory, it looks good since it’s used by a top website, still maintained and is less intrusive by having less magic behind the scene. That said, most problem that I was hitting with Entity Framework was with DbContext and Dapper.Net simply doesn’t have central object that they to be intelligent. In this process of introducing Dapper.Net, I’ll keep Entity Framework generating the database and keep this one to read the data. I will just not use it to save problematic entities.

Using Dapper.Net is simple. You use a Nuget package, the DLL is downloaded and you can use it. Not a lot of knowledge is require to use the basic because you are using a basic DbConnection which is enhanced with static method (by extension) to let you query and execute SQL code. However, this assumption that it does not require a lot of knowledge ends soon when you try to do something a little bit out of scope of a quick get and set. The first caveat is that the documentation is very slim. One can say that it doesn’t need more, but this is only true with basic scenario. To help explaining some limitations, this article will use an example where we will use Dapper.Net to save an entity called Contest. It’s a class that has properties which are object. Some of them are saved directly in the “Contest” table — this is called “Complex Object” in Entity Framework. And some others are saved in other tables with a foreign key that link them. It as 3 optional properties in a 0 to 1 relationship which go to other table, also it has two 0 to many (0-*) relationship to other classes. It also have a 1 to 1 relationship that is required. The number of columns in the “Contest” class is about 20. Here is a high level of the class diagram of the Contest class. I added some blue inside the aggregation to illustrate complex object and the white one are for classes that are from different tables than the contest ones.

2015-11-23 09.08.57

The second Dapper.Net limitation you will get is about the concept of complex object. Dapper.Net doesn’t know about it, which is fair since it’s a EF concept, but the problem is how can you tell this ORM to map a specific syntax into an object instead of a field directly into the main object (Contest). This is no where in the documentation. Here is two examples that you can see in almost every system: name that is localized and money with currency.

ComplexObjectDapper

Dapper.Net is expecting to have properties named Name_French, Name_English, InitialCapital_Value, InitialCapital_CurrencyTypeId. But, the problem is that Contest has 1 property called Name of type LocalizedString which has 2 properties called French and English and 1 property called InitialCapital of type Money which has 2 properties Value and CurrencyTypeId. This doesn’t work at all. The way to work with Complex Type (and also relationship tables) with Dapper is to use the multi mapping feature. This multi mapping allows you to divide a single database rows into different objects by defining a key pattern which is by default id. In our example, we could specify that the pattern is Name_French and InitialCapital_Value to be the cue to create a new object. The problem is not obvious first but is the limitation that Dapper.Net ORM decided to be built. You cannot have more than 7 mappings. The problem is even more huge when you learn that multi mapping is also the way to work with relationship (join). In our case, just the 1-1, the three 0-1 and the two 0-* take 6 of the multi mapping. This constraint is even worse when you think that some objects in relationship to the main one (contest) may also need to be loaded (multiple inner joins). For example, you are loading a Contest, that as a relationship to a list of User that participate which has a relation to a list of reputations which has a complex object for the type of reputation. Right there, we are using 3 mappings (contest->user, user->reputation, reputation->type). Very fast you hit the 7 multi mapping limitation of Dapper. The limitation of 7 multi-mappings is very arbitrary and could and should be unlimited. In SQL, you can create as much joins as you want and the ORM should follow that principle.

So, a way to get around limitation, you can create multiple select queries at a performance price. What is surprising is that few years ago, this limitation was raised and even got a pull request sent to the team to fix the 7 mappings. This one got rejected. Stackoverflow.com has a limited set of tables and not a rich business domain of classes as many enterprise software. This may justify that in their opinion that 7 multi-mappings is enough. That said, working with Dapper and Entity Framework can become more challenging than expected.

A second approach to work with a lot of complex type and relationship is to use a different class which flatten every table fields and then, using a more conventional classes mapping like AutoMapper to map the flatten table class into your rich domain class. However, this come with the cost of having more classes, the cost of having more mapping and finally, more unit test to write to ensure that the data goes from one place to the other one correctly.

A third approach is to use the basic dynamic query which return a row of object. You need to cast each fields but also you need to handle multiple rows from your joins. At that point, you are almost doing what you would do with ADO.Net.

A third Dapper.Net limitation is that it is not possible to configure this one to be wiser. Without being bloated like Entity Framework and without having to have a DbContext, Dapper.Net could have been a little more wise for mapping. You know ORM end with a “M” for “Mapping”.

Overall, Dapper.Net can work in parallel of Entity Framework as long as you adapt some of your SQL habits. Reducing the number of join, doing multiple queries, etc. I found it easier to start including Dapper.Net for write scenario than read scenario. This is justify by the fact that most of my insert and update scenario where for 1 entity at a time which just require me to create a basic SQL insert or SQL update query, copy and paste it inside a String variable and bind the object into it and execute. Yet, my current experience with Dapper.Net is mitigated and I feel that the .Net environment still have some place for improvement in the ORM area.

How to cache Html element with Html5 Canvas and LocalStorage

This caching idea is limited to some scenarios where you have defined html zones. For example, if you are working with dynamic charts or having a complex logic to render Html controls or even complex canvas. In all cases, the goal is to show to the user something relevant instead of just a loading spinner for the subsequent calls.

The concept is that once you have loaded the zone the first time, you take a snapshot and store it into the local storage as a visual copy — an image. The idea behind using an image is that you do not have to wait for extra dependencies like JavaScript files, CSS stylesheets or Ajax calls to have something displayed to the user. Meanwhile, the normal flow of your application must continue to have the real html loading with fresh values from the backend. The major advantage of that solution is that you have no slowness because of the network, neither of the server or from complex rendering client side code — you get it directly pre-rendered from the client side cache.

This article will show you how to take a screenshot,in JavaScript, with the library called Html2Canvas, stores it into the local storage and how to do the swap between this fake cached view with the real Html one. First, let’s create an example scenario with two zones. One zone, called box1 will take 3 seconds to load and the box2 will take 5 seconds. The goal is to display the fake cached view when loading the real data, and thus make it appearing to the user as it was already loaded.

This solution is having some caveats. First, if the time between the image and the real html takes too much time, than the user may be confused. Thus, this is a good solution for improving a solution that has good performance on the start. I would say that you can easily use that solution for zones that load without this trick under 3-5 seconds. You can always have a subtle loading visual hint somewhere if it’s longer. Second, if the cached image is very different from the new visual, than it will flicker during the swap of the image to the real html. Third, the local storage is limited in size, between 5 to 10 megs. That mean that you are limited to what you can cache with that solution. Overall, this solution is not a silver bullet and should be used as a blazing fast boost experience to an experience that is reasonable to load. It’s not a solution to apply to a menu for example, because user may interact very fast with it and would hit a wall or should I say an image. It’s good for element in a page that has a high visual interest or with element that has basic interaction like linking to somewhere else because the image can fulfill that contract.

Let’s dive in the code.

<div id="box1" class="box"><i class="fa fa-spinner fa-spin"></i></div>
<div id="box2" class="box"><i class="fa fa-spinner fa-spin"></i></div>
.box{
    width:150px;
    height:150px;
    background-color:lightblue;
    margin:10px;
}

.box i{
    font-size: 3em;
}

This will produce the following html :
2LoadingZonesBoxes

The normal loading experience is done by using JavaScript’s timeout. Both box display a different text, one display also an image.

setTimeout(renderBox1, 3000);
setTimeout(renderBox2, 5000);
function renderBox1()
{
    var $box = $('#box1');
    $box.text('Loaded 1');
    var $icon = $('<i>').addClass('fa fa-hand-peace-o');
    $box.append($icon);
}

function renderBox2()
{
    var $box = $('#box2');
    $box.text('Loaded 2');
}

So far, it takes the amount of time defined to see the spinner being removed and replaced by the real content.

The next step is to have in those render box the code to set the cache with the image of the rendered result. Box 1 will have the text and the image, box 2 just the text. This require to change both render method to call a cache method. Since the experience is so seamless that we will also, for the purpose of this demo, add some text under the boxes once refreshed with real value. So, the visual will be on the first call that we display the spinner, we cache, we display the text under the box. On the second load, we do not display the spinner since this one is replaced by the cached version right away. The texts under the boxes are shown only after the delay with the real value.

function renderBox1()
{
    var $box = $('#box1');
    $box.text('Loaded 1');
    var $icon = $('<i>').addClass('fa fa-hand-peace-o');
    $box.append($icon);
    cache($box);
    $box.after('<p>Not cached data box 1 in place</p>');
}

function renderBox2()
{
    var $box = $('#box2');
    $box.text('Loaded 2');
    cache($box);
    $box.after('<p>Not cached data box 2 in place</p>');
}

We also need to change the loading code to go check in the cache. We try to read the cache, the local storage, and if nothing

var box1Cached = unDataToCanvas(localStorage.getItem('box1'));
var box2Cached = unDataToCanvas(localStorage.getItem('box2'));

if(box1Cached){
     $('#box1').html(box1Cached);
}
setTimeout(renderBox1, 3000);


if(box2Cached){
    $('#box2').html(box2Cached);
}
setTimeout(renderBox2, 5000);

Finally, the load and save in the cache need to be coded. The save will create a canvas and we will store the data into a base64 representation. This is done by calling the method “toDtaUrl()” method.

function cache($elementToCache)
{
    var id = $elementToCache.attr('id');
    html2canvas($elementToCache, {
          onrendered: function(canvas) {
            window.localStorage.setItem(id, canvas.toDataURL());
          }
        });
}

The loading is more tricky. We need to load from the local storage the base64 image, create an image with the source to this base64 and return it.


function unDataToCanvas(data) {
    var img = new Image();
    var canvas = document.createElement('canvas');
    img.onload = function() {
        canvas.width = 150;
        canvas.height = 150;
        canvas.getContext("2d").drawImage(img, 0, 0);
    };
    img.src = data;
    if(data){
    	return img;
    }
    else{
    	return null;
    }
}

The whole code with a working demo is on JsFiddle.net : http://jsfiddle.net/mrdesjardins/31qca6b0/.

How to Create a Simple Circuit Breaker in C#

This article demonstrates how to create a simple Circuit Breaker in C#. It’s under 180 lines of code. Before going into detail, let’s take one paragraph to explain the circuit breaker pattern.

Circuit Breaker pattern is named from house circuit breaker — something fail, it opens the circuit, thus does not do any damage. With code, the circuit breaker pattern is useful when we access a resource that can slow down the system. For example, you have a website that connect different services on different servers, and one server goes down. You do not want subsequent calls to that server to call all the time that server. Mostly because it will slow down the whole website by having to timeout before being able to continue the code. This is a simple example, but this can be applied to other scenarios. The principle remains the same: execute some code, if it works nothing change; if it fails than we put the system into a close state. The open state allows to skip trying to execute the failed code and do something else until the circuit goes into an alternate state where it tries again to see if it can close the circuit again.

The circuit breaker demonstrates, here, does not rely on a timer. A lot of time, this pattern is closed for an amount of time before trying to re-open. However, I do not like having a timer running so I simply prefer to keep the date time and check when attempting to call. I also like using the state pattern for this circuit breaker pattern since we are moving through 3 different states (opened, half opened, closed).

The first class is the one that each of the three classes will inherit. It contains some virtual methods that the state can define if required. It also has a constructor that take the circuit breaker class to let you access some methods like increasing the default count when an exception is rose.

public abstract class CircuitBreakerState
{
    protected readonly CircuitBreaker circuitBreaker;

    protected CircuitBreakerState(CircuitBreaker circuitBreaker)
    {
        this.circuitBreaker = circuitBreaker;
    }

    public virtual CircuitBreaker ProtectedCodeIsAboutToBeCalled()
    {
        return this.circuitBreaker;
    }
    public virtual void ProtectedCodeHasBeenCalled() { }
    public virtual void ActUponException(Exception e) { circuitBreaker.IncreaseFailureCount(); }

    public virtual CircuitBreakerState Update()
    {
        return this;
    }
}

The first state is the open state. It contains the time where the state open (where something went wrong). When the state is instantiated, it set the current date time. This is required because if we want to update the state to the next one, the half-open one, we need to have at least a down period defined by the circuit breaker. It overrides two methods. The ProtectedCodeIsAboutToBeCalled where it tries to call the protected code. By protected code, I mean the code that is under the circuit breaker. This method try to update the state. If it can, than it goes into the half open state. Otherwise, it remains open.

public class OpenState : CircuitBreakerState
{
    private readonly DateTime openDateTime;
    public OpenState(CircuitBreaker circuitBreaker)
        : base(circuitBreaker)
    {
        openDateTime = DateTime.UtcNow;
    }

    public override CircuitBreaker ProtectedCodeIsAboutToBeCalled()
    {
        base.ProtectedCodeIsAboutToBeCalled();
        this.Update();
        return base.circuitBreaker;
    }

    public override CircuitBreakerState Update()
    {
        base.Update();
        if (DateTime.UtcNow &amp;gt;= openDateTime + base.circuitBreaker.Timeout)
        {
            return circuitBreaker.MoveToHalfOpenState();
        }
        return this;
    }
}

The second state is half open. This state is active only when the circuit went into something wrong and it tries to come back into an open one. This one is pretty small. It goes into open state if something is wrong, or it goes into the closed state if no exception is thrown. So, this is called after the threshold of time is done, it tries to close the circuit by calling the protected code. If this one fail, it creates a new open state where the time is resetted.

public class HalfOpenState : CircuitBreakerState
{
    public HalfOpenState(CircuitBreaker circuitBreaker) : base(circuitBreaker) { }

    public override void ActUponException(Exception e)
    {
        base.ActUponException(e);
        circuitBreaker.MoveToOpenState();
    }

    public override void ProtectedCodeHasBeenCalled()
    {
        base.ProtectedCodeHasBeenCalled();
        circuitBreaker.MoveToClosedState();
    }
}

The last state is the closed state. This is the first state you start the circuit pattern and the one you want to be. This is where we set the failure count and move to open when the failure count reach the threshold is met. This is not absolutely required, but it lets you have some window before opening the circuit. For example, sometime the connection can just be not reliable and retrying right after make it works. So you can set a failure threshold of 3 failures before going in an open state for 5 minutes.

public class ClosedState : CircuitBreakerState
{
    public ClosedState(CircuitBreaker circuitBreaker)
        : base(circuitBreaker)
    {
        circuitBreaker.ResetFailureCount();
    }

    public override void ActUponException(Exception e)
    {
        base.ActUponException(e);
        if (circuitBreaker.IsThresholdReached())
        {
            circuitBreaker.MoveToOpenState();
        }
    }
}

The biggest piece of code is inside the main class : the CircuitBreaker. This class contains the threshold for failure, the time to wait when the circuit is open and also all the locking mechanism.

public class CircuitBreaker
{
    private readonly object monitor = new object();
    private CircuitBreakerState state;

    public CircuitBreaker(int threshold, TimeSpan timeout)
    {
        if (threshold &amp;lt; 1)
        {
            throw new ArgumentOutOfRangeException(&amp;quot;threshold&amp;quot;, &amp;quot;Threshold should be greater than 0&amp;quot;);
        }

        if (timeout.TotalMilliseconds &amp;lt; 1)
        {
            throw new ArgumentOutOfRangeException(&amp;quot;timeout&amp;quot;, &amp;quot;Timeout should be greater than 0&amp;quot;);
        }

        Threshold = threshold;
        Timeout = timeout;
        MoveToClosedState();
    }

    public int Failures { get; private set; }
    public int Threshold { get; private set; }
    public TimeSpan Timeout { get; private set; }
    public bool IsClosed
    {
        get { return state.Update() is ClosedState; }
    }

    public bool IsOpen
    {
        get { return state.Update() is OpenState; }
    }

    public bool IsHalfOpen
    {
        get { return state.Update() is HalfOpenState; }
    }

    internal CircuitBreakerState MoveToClosedState()
    {
        state = new ClosedState(this);
        return state;
    }

    internal CircuitBreakerState MoveToOpenState()
    {
        state = new OpenState(this);
        return state;
    }

    internal CircuitBreakerState MoveToHalfOpenState()
    {
        state = new HalfOpenState(this);
        return state;
    }

    internal void IncreaseFailureCount()
    {
        Failures++;
    }

    internal void ResetFailureCount()
    {
        Failures = 0;
    }

    public bool IsThresholdReached()
    {
        return Failures &amp;gt;= Threshold;
    }

    private Exception exceptionFromLastAttemptCall = null;

    public Exception GetExceptionFromLastAttemptCall()
    {
        return exceptionFromLastAttemptCall;
    }

    public CircuitBreaker AttemptCall(Action protectedCode)
    {
        this.exceptionFromLastAttemptCall = null;
        lock(monitor)
        {
            state.ProtectedCodeIsAboutToBeCalled();
            if (state is OpenState)
            {
                return this; // Stop execution of this method
            }
        }

        try
        {
            protectedCode();
        }
        catch (Exception e)
        {
            this.exceptionFromLastAttemptCall = e;
            lock(monitor)
            {
                state.ActUponException(e);
            }
            return this; // Stop execution of this method
        }

        lock (monitor)
        {
            state.ProtectedCodeHasBeenCalled();
        }
        return this;
    }

    public void Close()
    {
        lock (monitor)
        {
            MoveToClosedState();
        }
    }

    public void Open()
    {
        lock (monitor)
        {
            MoveToOpenState();
        }
    }
}

This is easier to use than you may think. You mostly just need to use AttemptCall. You need to instantiate the circuit breaker. This must be a singleton since we want to keep the count of failure and the time when the circuit is open.

private static readonly CircuitBreaker circuitBreaker = new CircuitBreaker(3, TimeSpan.FromMinutes(15));

Then, we need to call the circuit breaker for the code that is susceptible to thrown an exception, to fail.

myCircuitBreaker.AttemptCall(()=&amp;gt;{yourCode();}).IsClosed?&amp;quot;AllFine&amp;quot;:&amp;quot;Something wrong&amp;quot;;

Of course, the example above is not really a normal situation — you wouldn’t just return a string. In real situation, you AttemptCall and get back the CircuitBreaker. Then, you have a switch case with the different state and act accordingly. For example, I usually on the IsClosed state get some log to try to improve the protected code if this one is getter there to often.

To conclude, this pattern is really easy to implement, easy to use and can help you in many situation instead of failing or having to repeat logic on failure code. You can use this pattern if you are having a caching system. If this one is down, you can go in the database. You can also use this pattern for third party API. If this one fail, you can notify the user that the system is temporary down without hammering the third party or you can use not fresh data for few minutes. You also have a lot of liberty about how to use this pattern. You can setup that it fails very fast with only a single try or being close for few milliseconds or few minutes depending of how reliable it the protected code. If you are interested in the code, the whole circuit breaker code is open source on GitHub. You can also use this one by using this Circuit Breaker Nuget package.

Check out the video Webucator made on my article: