XmlDocument.Load is evil

2007-11-20 @ 02:19#

i finally tracked down and fixed a nagging bug in the exyus engine tonight. it goes like this:

whenever i would run tests against my local machine, all went fine for the execution. however, when i attempted to modify the XSLT document tied to a recent request, i'd get an alert from the editor that the file was locked by another process. bummer.

turns out i had been using the XmlDocument.Load method to load the XSLT file from disk. bad idea! seems the XmlDocument can leave a lock on the file that may not get cleared until the parent process is cleared (i.e. restarting the ASP.NET process) or the XmlDocument is finally GC'ed. both unreliable.

the fix was quite easy, though. what i did was open the file using the XmlTextReader class and pass the reader to the XmlDocument. even better, i did this within a C# using block to make sure it gets disposed and marked for GC efficiently. here's my new code for loading the XSLT file from disk:

// use the supplied writer object
public void Execute(XmlDocument xmldoc, string xslFileName, ref XmlTextWriter output)
{
    // transform it and send it out
    Utility util = new Utility();
    MvpXslTransform xsldoc = new MvpXslTransform();
    xsldoc = (MvpXslTransform)System.Web.HttpContext.Current.Cache.Get(xslFileName);
    if (xsldoc == null)
    {
        xsldoc = new MvpXslTransform();
        XmlResolver xmlres = new XmlUrlResolver();
        using (XmlTextReader xtr = new XmlTextReader(xslFileName))
        {
            xsldoc.Load(xtr, new XsltSettings(true, false), xmlres);
            xtr.Close();
        }

        System.Web.HttpContext.Current.Cache.Add(
            xslFileName,
            xsldoc,
            new System.Web.Caching.CacheDependency(xslFileName),
            System.Web.Caching.Cache.NoAbsoluteExpiration,
            System.Web.Caching.Cache.NoSlidingExpiration,
            System.Web.Caching.CacheItemPriority.Default,
            null);
    }

    xsldoc.Transform(
        new XmlInput(xmldoc.CreateNavigator()),
        util.GetXsltArgs(),
        new XmlOutput(output)
        );
}

notice i am also caching the (compiled) instance of the XSLT document. since i am running all this in ASP.NET, i get a new instance for every request. that means i'd have to compile the sample XSLT document unlimited times. by caching it to memory after the compile, i only need to do this once for the life of the ASP.NET process (or until the underlying XSLT file is changed).

code