Customizing the minification of JavaScript in ASP.Net MVC4, allowing reserved words

A friend of mine just posted a question on StackOverflow, regarding having reserved member names in code, which doesn’t play well with the minifier. I opened up object explorer and started to look over the API in WebGrease and the System.Web.Optimazation framework. Just took a few minutes and put together some prototype code that will replace the reserved member name in the code, from e.g “delete” to “fooDelete”. It just doesn’t change the member names, but it will also update the usages of them as well. Note! Really not sure why you would like to do this though. Reserved is usually reserved by a meaning. Also note. I didn’t put any love into the code. It should just work. It’s a StackOverflow question, right?

First lets define a custom bundle that we will use only for the code that we want to apply our changes to. You would use this class when registrering your bundles instead of ScriptBundle.

public class CustomBundle : ScriptBundle
{
    public CustomBundle(string virtualPath) 
        : base(virtualPath)
    {
        this.Builder = new CustomBuilder();
    }
    public CustomBundle(string virtualPath, string cdnPath) 
        : base(virtualPath, cdnPath) {
        this.Builder = new CustomBuilder();
    }
}

Now lets define a custom builder that does the work. The key is the usage of JSParser found in WebGrease.

public class CustomBuilder : IBundleBuilder {
    public string BuildBundleContent(
        Bundle bundle, BundleContext context, IEnumerable<FileInfo> files)
    {
        var content = new StringBuilder();
        foreach (var fileInfo in files)
        {
            var parser = new Microsoft.Ajax.Utilities.JSParser(Read(fileInfo));

            //The magic happens here
            parser.Settings.AddRenamePair("delete", "fooDelete");

            content.Append(parser.Parse(parser.Settings).ToCode());
            content.Append(";");
        }

        return content.ToString();
    }

    private string Read(FileInfo file)
    {
        using(var r = file.OpenText())
        {
            return r.ReadToEnd();
        }
    }
}

That’s it. And again. Not much love in this code. But I tested to minifi with it, and it will take care of:

var myObj = {delete: function(){
    ... ... ...
}};

var myOtherObj = { test: function(){
    myObj.delete();
}};

and turn it to:

var myObj = {fooDelete: function(){
    ... ... ...
}};

var myOtherObj = { test: function(){
    myObj.fooDelete();
}};

Any use of the real delete will not be affected, which is exactly what we want. Also note, the result of the code above only kicks in when it’s minified. If you want it always, you’ll have to tweak it.

As an alternative solution you perhaps should use the minifier instead. That will look something like this:

public class CustomBuilder : IBundleBuilder
{
    public string BuildBundleContent(Bundle bundle, BundleContext context, IEnumerable<FileInfo> files)
    {
        var settings = new CodeSettings();
        settings.AddRenamePair("delete", "fooDelete");

        var content = new StringBuilder();
        foreach (var fileInfo in files)
        {
            var minifier = new Microsoft.Ajax.Utilities.Minifier();
            content.Append(minifier.MinifyJavaScript(Read(fileInfo), settings));
            content.Append(";");
        }

        return content.ToString();
    }

    private string Read(FileInfo file)
    {
        using (var r = file.OpenText())
        {
            return r.ReadToEnd();
        }
    }
}

I also have to say something negative about the bundler. When it failed, it failed in silence, just injecting a comment in the generated JS and then didn’t minify the JavaScript. Come on. That should fail with a big fat BOOOOOM!

//Daniel

RouteCollection.LowercaseUrls .Net 4.5

It’s such a good feeling to reduce code. Previously I’ve been using some custom code to get lower case routes in my ASP.Net MVC apps. There’s also a NuGet package for it. But if your ASP.Net MVC site runs on .Net 4.5, there’s now a much simpler way:

routes.LowercaseUrls = true;

Away goes this old code (thanks to @leedumond) which has the code on CodePlex:

public class LowerCaseRoute : Route
{
    //Constructors removed for clarity

    public override VirtualPathData GetVirtualPath(RequestContext requestContext, RouteValueDictionary values)
    {
        var path = base.GetVirtualPath(requestContext, values);

        if (path != null)
        {
            var virtualPath = path.VirtualPath;
            var indexOf = virtualPath.IndexOf("?");

            if (indexOf > 0)
            {
                var leftPart = virtualPath.Substring(0, indexOf).ToLowerInvariant();
                var queryPart = virtualPath.Substring(indexOf);
                path.VirtualPath = string.Concat(leftPart, queryPart);
            }
            else
                path.VirtualPath = path.VirtualPath.ToLowerInvariant();
        }

        return path;
    }
}

and the extension method

public static class RouteExtensions
{
    public static Route MapLowerCaseRoute(this RouteCollection routes, string name, string url, object defaults)
    {
        var defaultDictionary = defaults == null
            ? new RouteValueDictionary()
            : new RouteValueDictionary(defaults);

        var route = new LowerCaseRoute(url, defaultDictionary, new MvcRouteHandler());

        if (string.IsNullOrWhiteSpace(name))
            routes.Add(route);
        else
            routes.Add(name, route);

        return route;
    }
}

Yet another step taken to reduce the code base.

//Daniel

IoC with SisoDb in ASP.Net MVC

I just put together a short screencast (about 4min) showing you have to configure SisoDb with an IoC-container in ASP.Net MVC using “One session per HttpRequest”. For this demo I will use Ninject.

The screencast is hosted in the SisoDb channel at Vimeo.

Updated: Mike Paterson has a GitHub repository with code for the episode, found here: https://github.com/devlife/Sandbox/tree/master/SisoDb

Summarized

After having installed the “Ninject.MVC3 NuGet package”, I added a NinjectModule named “DbConfig” under a new folder/namespace “IoCConfig”.

public class DbConfig : NinjectModule
{
    public override void Load()
    {
        var db = "CoreConcepts".CreateSql2012Db();
        Kernel.Bind<ISisoDatabase>()
            .ToMethod(ctx => db)
            .InSingletonScope();

        db.CreateIfNotExists();

        Kernel.Bind<ISession>()
            .ToMethod(ctx => db.BeginSession())
            .InRequestScope();
    }
}

After that we just need to ensure the module is loaded by Ninject in the bootstraper, which is installed by the Ninject.MVC3 NuGet, under “App_Start”. The only change that is needed is adding one row to the “CreateKernel” member, so that it look like this:

private static IKernel CreateKernel()
{
    var kernel = new StandardKernel();
    kernel.Bind<Func<IKernel>>().ToMethod(ctx => () => new Bootstrapper().Kernel);
    kernel.Bind<IHttpModule>().To<HttpApplicationInitializationHttpModule>();

    //ADD THIS TO LOAD OUR MODULE(s)
    kernel.Load(typeof(MvcApplication).Assembly);

    RegisterServices(kernel);
    return kernel;
}

We are now all set and can take a dependency on “ISession” in our controllers. Either in the constructor or by resolving it using a static class/method/service locator concept against the Ninject IoC-container. Since my sample uses Db-access in each action, I used constructor injection.

public class CustomerController : Controller
{
    private readonly ISession _dbSession;

    public CustomerController(ISession dbSession)
    {
        _dbSession = dbSession;
    }

    //...
}

That’s it.

//Daniel