Friday, 30 March 2012

Language selector in ASP.NET MVC 3 - Part 3

In the last post I talked about implementing a custom route in order to improve the route handling work to support multiple languages in a web application. This custom route would remove the need of duplicate the route (with and without the language parameter) and in the first part of this series I showed an example where the view rendered the language selector using ActionLink for generating the urls, and the collection to iterate was expected to be as part of the view model.

But this is not always possible to do, in fact, the most common scenarios I’ve dealt with, involve putting this kind of component in a common place such as the Layout page or even a partial which is included in Layout page, but less frequent in a dedicated view. To achieve this separation, it’s common to use a Child Action for invoke a controller, which is going to execute an action and return usually a partial view.

If we take the solutions previously discussed and make a little refactoring: create an action (LanguageList) in the languages controller and move the code that renders the links to a partial view (_langSelector) and go to layout page and invoke it as:

@Html.Action("LanguageList", "Langs")

Sure you are going to see the results. The controller action should be like this:

[ChildActionOnly]
public ActionResult LanguageList()
{
    var langs = LanguageProviders.Current.GetList();
    return PartialView("_langSelector", langs);
}

The HTML rendered looks like this:

<a href="/Langs/LanguageList">English</a> 
<a href="/es-ES/Langs/LanguageList">Espa&#241;ol</a> 
<a href="/fr-FR/Langs/LanguageList">Fran&#231;ais</a> 

As you can see, the urls always point to controller Langs and action LanguageList instead of pointing to current controller and action, why does it happen?

When we invoke a child action, a new request is started to the target action, that means, when the route asks for current request values, it obtains those from the new request, which means, the “original” route values were lost at the moment of making the child request.

As these values were lost, my solution is simply to provide them via parameter at invocation time. Once in the controller, the values must be passed to the view, may be using the ViewBag or even a more sophisticated solution could involve the creation of a view model with these data ready to be read by the view.

Having said that, here is the quick solution:

The controller action

[ChildActionOnly]
public ActionResult LanguageList()
{
    ViewBag.CurrentValues = RouteData.Values["CurrentValues"];
    var langs = LanguageProviders.Current.GetList();
    return PartialView("_langSelector", langs);
}

At this point and only to remark, the attribute ChildActionOnly is strongly recommended in order to avoid this action to be called directly or even from an AJAX request if this is not intended to.

The invoke statement in the layout page.

@Html.Action("LanguageList", "Langs", 
 new { CurrentValues = ViewContext.RouteData.Values }
)

The goal of this new parameter is to pass the real current route values to the controller, so it will be able to grab them and pass to the view.

The partial view

@{
    var currentValues = (RouteValueDictionary)ViewBag.CurrentValues;
    var tempValues = new RouteValueDictionary();
    foreach (var item in currentValues)
    {
        tempValues.Add(item.Key, item.Value);
    }
}

@foreach (var lang in Model)
{
    tempValues["lang"] = lang.Code;
    
    @Html.ActionLink(lang.Name, null, tempValues)
    @Html.Raw(" ")
}

At this point, I consider important the lines that make a copy (clone) of the route data received from the controller, otherwise the route values should be modified inside the loop that follows it, and as a consequence, the dictionary will affect the rest of routes in the page, setting the last value in lang.Code forever in the route values if this value were not supplied explicitly when generating the links.

Well, I hope this little sand grain will help you to your day-to-day web programming task, any comment and ideas will be welcome.

Tuesday, 20 March 2012

Language selector in ASP.NET MVC 3 - Part 2

A few weeks ago I talked about giving support to multi lingual applications or i18n for brevity. There I mentioned the strategy I followed using two routes (one including the language parameter and another is just the default) and also I promised to talk more about routes, specially about routes whose goal is match with or without the language parameter. Well, actually I wasn’t too happy with that implementation, but if it isn’t broken, don’t fix it. However, something deep in my mind suspected there must be another solution, I mean, more elegant, while making google research I found an interesting article in codeproject part 1 and part 2. Nice try! But my question was: why to make a custom route class and finally to define the two different routes again at application startup?
I took it as starting point and worked on it, the result: a custom route which is able to deal with the dirty stuff about prepending properly the language fragment and matching from the request url.
The idea is to inherit from the Route class instead of RouteBase, I want to take advantage of the base implementation which is responsible of doing the really dirty stuff, I won’t reinvent the wheel. So the methods to override are GetRouteData and GetVirtualPath.
The custom route must know the default language, it has it as parameter, some comments on GetRouteData method. First, the goal for this method is to determine if the current request must be parsed by us or let it go. Manually split by / and remove the ~ element, after that if there are more than one segment and the first segment doesn’t match the regular expression ^[a-z]{2}(-[A-Z]{2})?$ (commented in the earlier post), then ask to base implementation and if it replies affirmative, set the route value indexed by language parameter to the default language. Otherwise, a little trick with the base implementation, save the current url and prepend {lang}/ to the current and ask to base implementation to check if is valid and finally restore the url value. As you can see, here is when I’ve done the work of two routes in one!
public override RouteData GetRouteData(HttpContextBase httpContext)
{
    var requestedURL = httpContext.Request.AppRelativeCurrentExecutionFilePath;
    var segments = requestedURL.Split('/').Where(x => x != "~").ToArray();

    // if request is not localized then call base
    if (segments.Length > 0 && !Regex.IsMatch(segments[0], @"^[a-z]{2}(-[A-Z]{2})?$"))
    {
        var result = base.GetRouteData(httpContext);
        if (result != null)
            result.Values[LangParam] = _defaultLang;
        return result;
    }

    // if request is localized then prepend the culture segment to the url
    var currentUrl = Url;
    Url = string.Format("{{" + LangParam + "}}/{0}", _url);
    var baseRoute = base.GetRouteData(httpContext);
    Url = currentUrl;
    // save and restore the current URL
    return baseRoute;
}
The next step to complete the route’s functionalities is GetVirtualPath which will generate the url according to route map data. Generate the url is a little harder than match the url, but not impossible. Let’s remember how the url generation occurs: if the dictionary received as parameter has all the necessary values for this route, the optional values may be taken from the current request (this will give us the idea of virtual directories commented in an earlier post). So here the important parameter is the language, find it either in current request or explicit values and remove it (the actual route doesn’t have this paramter). Call base implementation and restore each case if necessary (this is important, or the next routes generated since this will have altered values). Finally to prepend language segment if necessary and only if its value is different from the default language. I show here the code.
public override VirtualPathData GetVirtualPath(RequestContext requestContext, RouteValueDictionary values)
{
    string lang = "", lnRequest = "", lnValues = "";
    // look for the language param in current request context
    if (requestContext.RouteData.Values.ContainsKey(LangParam))
    {
        // if found then save it in lang variable
        lnRequest = lang = (string)requestContext.RouteData.Values[LangParam];
        // and remove it
        requestContext.RouteData.Values.Remove(LangParam);
    }

    // look for the language param in explicit values 
    if (values.ContainsKey(LangParam))
    {
        lnValues = lang = (string)values[LangParam];
        values.Remove(LangParam);
    }

    // call base method...
    var virtualPath = base.GetVirtualPath(requestContext, values);

    // restore from current request context if necessary
    if (!string.IsNullOrWhiteSpace(lnRequest))
        requestContext.RouteData.Values.Add(LangParam, lnRequest);
    // restore from explicit values if necessary
    if (!string.IsNullOrWhiteSpace(lnValues))
        values.Add(LangParam, lnRequest);

    if (virtualPath == null) return null;

    // prepend language segment if necessary and only if different from the default language
    if (!string.IsNullOrWhiteSpace(lang) && !string.Equals(lang, _defaultLang, StringComparison.OrdinalIgnoreCase))
    {
        virtualPath.VirtualPath = lang + "/" + virtualPath.VirtualPath;
    }
    return virtualPath;
}
Now, it’s time to invoke this brand-new route just implemented, I think it should be as similar as possible to use as the out of the box route, like this:
routes.AddLocalizedRoute(
    "es-ES",                                                                        //The default Language 
    "{controller}/{action}/{id}",                                                   // URL with parameters (Without any sign of i18n)
    new { controller = "SimpleUser", action = "Index", id = UrlParameter.Optional } // Parameter defaults
);
In order to achieve this I’ve implemented an extension for the class RouteCollection, like the original MapRoute which is an extension too.

Tuesday, 13 March 2012

Handling Excel spreadsheets XLS and XLSX with dot NET

More than once in my projects the format for input and/or output is Excel spreadsheet either using Excel 97-2003 (.xls) or Excel 2007 OOXML (.xlsx). The open source community contains several libraries, for legacy files the most famous is NPOI, which in fact is the .NET version of POI Java project at http://poi.apache.org/. The result about modern OOXML were more diverse, ExcelPackage was the first but this project was abandoned late 2010. However this code wasn’t to the trash, EPPlus is the evolved version based on it and strongly optimized by the new team.
If we want to use indiscriminately the two formats, it’s necessary to have updated the two libraries and since they are developed by two different teams, they have two completely different interfaces and ways to be used. Despite the fact it’s open source and the hard work is done, it’s a mess to deal with this difference in our code.
Start writing if-else statement alongside the code is not an option! This will get us to Spaghetti code anti-pattern and the consequent difficulties for maintaining the resulting software. So the design patterns to the rescue. The quick solution for the immediate needs, it’s necessary a common interface for basic operation with Excel files and we have two different implementations. The adapter pattern (aka wrapper) is the best match. The interfaces created are:
public interface IExcelHandler : IDisposable
{
    void LoadExelFile(string fileName);
    IExcelSheet GetSheet(int index);
    void Save();
    void Save(string filename);
}

public interface IExcelSheet
{
    string GetCellValue(int row, string column);
    void SetCellValue(int row, string column, object value);
}
 
The xml comments were removed for brevity but the methods’ names are self-explanatory (or at least I think so), the only thing might be confusing is the column parameter which is defined as string because it’s intended to be invoked with the actual Excel column name, such as A, B, etc. The operations defined above were enough in order to solve my needs; I know this might be extended for more complex scenarios but this is a first version and I don’t like to make over engineering or as we say in my town: killing mosquitos with cannons.
Having the common interface and all the implementations is not enough for a reusable component and abstract the client from the concrete implementation, how is to be created the instances of each implementation according to the file format? Design patterns again, the Factory Method may be one solution, in fact I made a simplified version using an interface that defines a Create method who is the responsible for create the concrete IExcelHandler instance.
public interface IExcelHandlerFactory
{
    IExcelHandler Create(string filename);
}
 
This interface might be injected via your favorite IoC Container and linked to the default implementation I provide in the class ExcelHandlerFactory or if you aren’t using dependency injection it can be used as a Singleton through the static property Instance.
As NPOI and EPPlus are already packages in the NuGet Gallery, I’ve done a package with these codes in order to contribute to the community and to be easier to bootstrap when handling Excel files. The package is available on NuGet feed at https://nuget.org/packages/ExcelHandler, just execute the following command at NuGet Package Manager Console:
Install-Package ExcelHandler
This will add automatically as dependencies the NPOI and EPPlus packages with all the corresponding references in the project. An example on how to use it is as follows:
Console.WriteLine("Opening and modifying a file with Excel 2007+ format...");
using (var excelHandler = ExcelHandlerFactory.Instance.Create(@"..\..\modern_file.xlsx"))
{
    var sheet = excelHandler.GetSheet(1);
    Console.WriteLine(sheet.GetCellValue(1, "A"));
    sheet.SetCellValue(1, "A", "Test value in A1 cell");
    excelHandler.Save();
}
Console.WriteLine("... done.");
Console.WriteLine("Opening and modifying a file with Excel 97-2003 format...");
using (var excelHandler = ExcelHandlerFactory.Instance.Create(@"..\..\legacy_file.xls"))
{
    var sheet = excelHandler.GetSheet(1);
    Console.WriteLine(sheet.GetCellValue(1, "A"));
    sheet.SetCellValue(1, "A", "Test value in A1 cell");
    excelHandler.Save();
}
Console.WriteLine("... done."); 
 
This example just opens two Excel files, read the content from the cell A1 and set some text in the same cell. Since the interface IExcelHandler inherit from IDisposable it can be used in a using statement in order to simplifying the resource disposing. The source code is available at https://bitbucket.org/abelperezok/excelhandler.