Sunday
Dec062009

Less CSS with .LESS and T4!

After hearing about .LESS for .NET I was ecstatic.  I’ve been looking for something like this for quite some time.  Recently Phil Haack blogged about a T4 template he made using Damien Guard’s helper class to generate CSS files for each LESS file.  This way it would generate static CSS files you could reference.

As great as this was I personally was looking to just have the CSS files appear in the location of the LESS files making it easier to reference/view the CSS files.  The advantage to this is if you have CSS files in a particular directory structure they will be exactly where the LESS files are thus keeping your structure.

For this I utilize T4 Toolbox which is a great library that helps with doing some normally painful tasks in T4 fairly easy.  An example of this is being able to have multiple output files and being able to set their locations which is what makes this possible.  To use this T4 template ensure you’ve installed the T4 Toolbox then just drop it in your project directory.  For the sake of making this work with Phil’s example the assembly references expect the .LESS and YUI Compressor libraries to be in the solution’s root directory.  You can change this to fit your needs.

<#@ template language="C#" hostspecific="True" #>
<#@ output extension="log" #>
<#@ include file="T4Toolbox.tt" #>
<#@ assembly name="$(ProjectDir)\..\dotless.Core.dll" #>
<#@ assembly name="$(ProjectDir)\..\Yahoo.Yui.Compressor.dll" #>
<#@ import namespace="dotless.Core" #>
<#@ import namespace="Yahoo.Yui.Compressor" #>
<#@ import namespace="System.IO" #>

<#
    bool compress = false;
#>

<#
    var currentDirectory = Path.GetDirectoryName(Host.TemplateFile);
    var lessFiles = Directory.GetFiles(currentDirectory, "*.less", SearchOption.AllDirectories);
    
    foreach (var lessFile in lessFiles)
    {
        string file = Path.Combine(Path.GetDirectoryName(lessFile), Path.GetFileNameWithoutExtension(lessFile) + ".css");
        
        Write("* Converting {0} to {1}", lessFile, file);
        
        LessTemplate template = new LessTemplate(lessFile, compress);
        template.Output.File = file;
        template.Render();
    }
#>

<#+
public class LessTemplate : Template
{    
    private static ExtensibleEngine DotLess = new ExtensibleEngine();
    
    protected bool Compress { get; set; }
    protected string File { get; set; }
    
    public LessTemplate(string file, bool compress)
    {
        File = file;
        Compress = compress;
    }
    
    public override string TransformText()
    {
        string css = DotLess.TransformToCss(File);
        
        if (Compress)
            css = CssCompressor.Compress(css);
        
        Write(css);
        return this.GenerationEnvironment.ToString();
    }
}
#>
Saturday
Aug012009

I'm over here!

I'd first like to thank the people that are still subscribed to my feed after being absent for so long.  Finding a full-time job was priority #1.  I'm glad to report I found one after only 2-3 weeks of searching.

I wound up getting a job with Curse, Inc.  If you play an MMO chances are you know who Curse is.  They own and operate sites like www.curse.com www.wowace.com www.worldofraids.com and so forth.  They're a great company with a lot of talent and direction.  I came onto the team during a project that is being built on ASP.NET MVC which is great since I've been following it since it's birth back in 2007.  It's a great oppurtunity and honor to work with Curse and my fellow co-workers.

I'd like to get started on an open-source project for the community that I can work on during the weekends so we'll see how that turns out.  But I do promise starting next week I'll start my regular posting again.

Monday
Jun012009

Where has all the content gone?

I feel bad with more and more people following my blog that I'm not able to keep up with my posts but I do have a reason, I promise!

I've been self-employed however due to clients drying up I've been looking for a full time job as a .NET dev and have been extremely busy with writing demo code and trying to find work.  If you know of anyone looking for a .NET dev feel free to forward them to me through my Contact page or e-mail me at chad dot moran at live dot com.

I have some great content writting up including the third part of my ASP.NET MVC URL generation optimization and another great post about how I found a problem with a common workaround for ASP.NET MVC output caching that could cause way more memory usage than required.

I'll try to pump out these posts as soon as possible and my apologies for not getting them out sooner.  Thanks for reading and hope to write again soon.

Saturday
May022009

Extension method digest #4 RouteBase.AsRoute

This one is very simple but is extremely helpful when dabbling in the System.Web.Routing namespace.  Using AsRoute extension will allow you to get to the properties and methods on the Route type itself like Constraints, Defaults and Url.

public static class RouteBaseExtensions
{
public static Route AsRoute(this RouteBase routeBase)
{
return routeBase as Route;
}
}

Again, a very simple extension method that can help save some time instead of having to cast the object yourself.  If the RouteBase target isn’t a Route itself it will just return null.

Thursday
Apr232009

Optimizing URL generation in ASP.NET MVC – Part 2

Part 1
Part 2

So in continuing with the series of URL generation optimization in ASP.NET MVC we’re going to take a look at the actual optimization part of things.

Before I get started I’d like to start off by saying the results of the first part’s test were a little off due to my mistake.  When I was setting up the routes I didn’t setup defaults for the default route (“/”) so the URLs actually being generated were /name=chad&age=23 instead of /Home/Index/Chad/23 and thus skewing the results so to fix that I’ll be running the first 5 all over again including 3 new ones.  Note that the expression syntax method was not changed from this and the performance still stands.  The only methods that were really affected by this were those that found the route by action and controller names.  I apologize for the mistake and I will update the post accordingly.

In the first part we looked at some methods that are probably most commonly used by ASP.NET MVC devs and this part I’d like to look at some lesser-used methods of getting these URLs generated.  In this part I’m going to go over 2 other methods of getting URLs generated.  Though these methods might not generate all of the HTML they’re just as useful and even faster than previous methods.

Url.Action using action name and anonymous object (Method 6)

Url.Action("Index", new { name = "Chad", age = 23 })

Url.Action using action name, controller name and anonymous object (Method 7)

Url.Action("Index", "Home", new { name = "Chad", age = 23 })

Url.RouteUrl using route name and anonymous object (Method 8)

Url.RouteUrl("HomeIndex", new { name = "Chad", age = 23 })

Same conditions as the the first part.  After thinking about it I should have put these tests into the first part but what can you do so here they are.  Before the results I’d like to point out these methods don’t generate the whole URL rather the part after the TLD.  So in a URL like http://mydomain.com/Home/Index these methods would only generate the /Home/Index parts.  I understand this isn’t the same as the previous methods but they can still be used the same.  Only differences is you would have to wrap them in your own HTML (blasphemy, I know).  It would look something like this.

<a href="<% Url.RouteUrl("HomeIndex", new { name = "Chad", age = 23 }); %>">Link</a>

Now for the results.

URL Generation test

As you can see there was actually a difference between using action name and controller name versus using route names in performance like I originally suspected there should have been.

This is where the second part of this series should have started considering those 3 last tests should have been in the last one.

In the first part I linked to a great presentation that showed off how you can improve URL generation performance by not using the expression syntax and not using anonymous objects.  I’d also like to cover that.

When you use method 1 for URL generation it has to get the parameter values from your expression and put them into a RouteValueDictionary and pass that into another method to get the URL.  The method it uses is a nasty one (not coded badly but, expensive).  This method is in Microsoft.Web.Mvc.Internal.ExpressionHelper class.

private static void AddParameterValuesFromExpressionToDictionary(RouteValueDictionary rvd, MethodCallExpression call)
{
ParameterInfo[] parameters = call.Method.GetParameters();
if (parameters.Length > 0)
{
for (int i = 0; i < parameters.Length; i++)
{
Expression expression = call.Arguments[i];
object obj2 = null;
ConstantExpression expression2 = expression as ConstantExpression;
if (expression2 != null)
{
obj2 = expression2.Value;
}
else
{
Expression<Func<object>> expression3 = Expression.Lambda<Func<object>>(Expression.Convert(expression, typeof(object)), new ParameterExpression[0]);
obj2 = expression3.Compile()();
}
rvd.Add(parameters[i].Name, obj2);
}
}
}

As you can see on lines 17 and 18 this is getting called for every parameter coming into your method.  If you have a method with more parameters performance would be affected.  I decided to run a test on this too and see how performance would be changed as you add parameters onto your actions.  Again, same conditions used as the all other tests.

Expression syntax test

As you can see performance gets thrown out the door as you add more and more parameters to the action method.

So that’s what is killing performance in the expression syntax of URL generation.  What about the other methods, what could be hurting their performance?  The main performance loss in all other methods is using anonymous objects.  When you use an anonymous object for your route parameter values it has to use reflection to put them into a RouteValueDictionary and this is how it’s done.

private void AddValues(object values)
{
if (values != null)
{
foreach (PropertyDescriptor descriptor in TypeDescriptor.GetProperties(values))
{
object obj2 = descriptor.GetValue(values);
this.Add(descriptor.Name, obj2);
}
}
}

So again, reflection is happening for each parameter passed in so the same performance decrease would happen with all other methods when adding more parameters.

To get around this we can just pass in our own RouteValueDictionary to avoid having to any reflection and it’s pretty easy to do.  So we’ll run the same 9 tests as before but without using anonymous objects and see what kind of difference we see.

Html.ActionLink<> expression (Method 1)

Html.ActionLink<HomeController>(c => c.Index("Chad", 23), "Link")

 

Html.ActionLink using action name and RouteValueDictionary (Method 2)

Html.ActionLink("Link", "Index", new RouteValueDictionary { { "name", "Chad" }, { "age", 23 } })

Html.ActionLink using action name, controller name and RouteValueDictionary (Method 3)

Html.ActionLink("Link", "Index", "Home", new RouteValueDictionary { { "name", "Chad" }, { "age", 23 } }, null)

Html.RouteLink using RouteValueDictionary (Method 4)

Html.RouteLink("Link", new RouteValueDictionary { { "name", "Chad" }, { "age", 23 } })

Html.RouteLink using named route and RouteValueDictionary (Method 5)

Html.RouteLink("Link", "HomeIndex", new RouteValueDictionary { { "name", "Chad" }, { "age", 23 } })

Url.Action using action name and RouteValueDictionary (Method 6)

Url.Action("Index", new RouteValueDictionary { { "name", "Chad" }, { "age", 23 } })

Url.Action using action name, controller name and RouteValueDIctionary (Method 7)

Url.Action("Index", "Home", new RouteValueDictionary { { "name", "Chad" }, { "age", 23 } })

Url.RouteUrl using route name and RouteValueDictionary (Method 8)

Url.RouteUrl("HomeIndex", new RouteValueDictionary { { "name", "Chad" }, { "age", 23 } })

TIme to run the tests…

URL Generation test

I put method 1 there for consistency.  The rest saw a pretty substantial boost in performance.  The most was Method 5 going from 282 to 120 ms, that’s more than double boost in performance.

As you can see the difference was going from this…

new { name = "Chad", age = 23 }

To this…

new RouteValueDictionary { { "name", "Chad" }, { "age", 23 } }

Again, I’m sure there are those out there that will point out this looks like a pain to write these types of links with this code and I agree.  That’s why in the next part I’m going to go over how we can get intellisense and maintainability over our code when generating URLs.  I am fully aware that this is only a difference of 100ms~ over 10,000 links but this can be very helpful with high-volume sites and sites that generate a large amount of URLs per page.

So now we’ve seen which methods are fastest and how we can make them twice as fast.  In the next part of the series I’ll go over how we can utilize this faster code and still keep it maintainable and easy to use.