james mckay dot net
because there are few things that are less logical than business logic

Sometimes, sweeteners can be a bit sour

There’s a gotcha with including scripts in your XSL stylesheets using the tag that I blogged about a couple of months ago.

Whenever you load in a stylesheet into an XslCompiledTransform object, it compiles the scripts into a new in-memory assembly using System.CodeDom. Every time you re-load the stylesheet, another assembly is created, and as assemblies are not garbage collected and can only be unloaded by unloading your entire application domain, this is a potentially pretty serious memory leak that can quickly bring your application to its knees.

According to Microsoft, this only affects ASP.NET 1.0, but people have reported problems with ASP.NET 1.1 and 2.0 through 3.5 as well.

There are two solutions to this. One is to use XSL extension objects instead of scripts. The other is to use a singleton design pattern, only creating your XslCompiledTransform once and caching it for future use. In fact, you should be doing this for performance reasons anyway, as parsing and compiling an XSL stylesheet takes time.

A class such as this should prove useful. It caches your XSL stylesheets so that each one will only be parsed into an XslCompiledTransform the first time:

using System;
using System.Collections.Generic;
using System.IO;
using System.Runtime.CompilerServices;
using System.Xml;
using System.Xml.Xsl;

public static class XslCache
{
    private static Dictionary<string, XslCompiledTransform> cache
        = new Dictionary<string, XslCompiledTransform>();

    [MethodImpl(MethodImplOptions.Synchronized)]
    public static XslCompiledTransform GetTransform(string xslFile)
    {
        // Normalise the path to the XSL file
        xslFile = new FileInfo(xslFile).FullName;

        // Return the cached transform if it exists.
        if (cache.ContainsKey(xslFile)) {
            return cache[xslFile];
        }

        // Otherwise, load the XSL stylesheet.
        var transform = new XslCompiledTransform();
        transform.Load(xslFile,
            new XsltSettings(true, true),
            new XmlUrlResolver()
        );

        cache.Add(xslFile, transform);
        return transform;
    }
}

XslCompiledTransform objects are threadsafe once they have been loaded.