Lingering temporary DLLs from XSLT with script.

I have an XSLT style sheet that includes some C# "script" to do some date rendering. Every time I run the transformation, I get a new DLL in my temporary directory that has the compiled script functions. The DLLs don't ever seem to get deleted, so I'm getting this pile of 4k DLLs.

Is this a bug in the .NET framework (I'm using version 2.0), or am I doing something wrong that is causing this

This is the relevant bit of code from my application:

XslCompiledTransform xslt = new XslCompiledTransform();
XsltSettings settings = new XsltSettings(true, true);
XmlTextReader reader = new XmlTextReader(GetStyleSheetStream(styleSheet));
xslt.Load(reader, settings, null);

// XmlTextWriter doesn't implement IDisposable or we'd use 'using'.
XmlTextWriter writer = null;

try
{
writer = new XmlTextWriter(outputFileName, null);

XsltArgumentList args = new XsltArgumentList();
args.AddParam("title", "", title);

// Note that without an XmlResolver, the XSLT document() function won't work.
xslt.Transform(planFileName, args, writer);
}
finally
{
if (reader != null)
{
reader.Close();
}

if (writer != null)
{
writer.Close();
}
}



Answer this question

Lingering temporary DLLs from XSLT with script.

  • boubounas

    Hi Surgey,

    Our application got the same logging dll issues from xslt with scripts, it eats up all the disk space. We also tried XslCompiledTransform.TempraryFiles.delete(). It still can not delete the dlls. However, If we use the obsolete XslTransform class, no dlll is logged into that temp folder. But according to MSDN support article 316775, the XslTranform has the memory issue with the framwork1.1. Does XslTransform has the same memory leaking issue with .net framework 2.0

    What will be the best way to solve this issue Using XslCompiledTransform and at the same time write a batch file to delete the dlls Or just use the obsolete XslTransform class Thanks.


  • GN

    I'll take a deepier look into the MvpXslTransform class it looks pretty cool.

    I agree the multiple overloads is a bit much but I didn't want to reduce any functionality of the XslCompiledTransform class.


  • Alan Singfield

    Do you really need to expose all these overloads

    Number of Load() and Transform() overloads is the major flaw in our design.

    Initially they were added for "user convenience" but I haven't saw user who liked the design. Most likely you need just one overload and don't need to pollute you program with the rest of the garbage.

    If you are looking to create universal wrapper good for multiple scenario you still may pick up several most fundamenal methods and ignore rest of overloads. (you don't have backwards compatibility restrictions and can't have polymorphism anyway).

    For Load it will be:

    Load(XmlReader stylesheet, XsltSettings settings, XmlResolver stylesheetResolver)

    For Transom:

    Transform(XmlReader input, XsltArgumentList arguments, XmlWriter results, XmlResolver documentResolver)

    Transform(IXPathNavigable input, XsltArgumentList arguments, XmlWriter results, XmlResolver documentResolver)

    We have two transforms because second one doesn't re-cache input document but don't allow stylesheets with whitespace stripping rules. (not perfect as well.)

    I also encourage you to take a look at MvpXslTransform class (http://mvp-xml.sourceforge.net/api/2.0/T_Mvp_Xml_Common_Xsl_MvpXslTransform.html).

    It would be nice to add temp file cleanup logic there as well, if this wasn't done already.



  • mbr_rptusr

    Thank you for that clarification. I suppose I could, at the price of increased memory footprint, only compile transformations once per application run and hang on to the objects, and slow the creation of temporary DLLs.

    I may also be able to use the reference you provided to eliminate this particular instance of scripting. But it still strikes me as odd that XslCompiledTransform not cleaning up after itself was by design. Is having my users run Disk Cleanup the other part of the design Should I try to find and delete the temporary DLL myself when I'm done with the XslCompiledTransform

    I'm just trying to find the correct way of using the framework such that all the resources allocated while the application is running are returned to the system when the application exits. Is there a way to do that other than not including script in my stylesheets


  • Altug Atik

    Answer on the question when app should delete its temp files depends on requirements to this app. In most cases it'd be ok to delete old temp files on app start. If you want to be absolutely kosher you can delete them on exit. The best way to do this is to run transformation part of your program or entire program in a separate app domain. Main app domain would just create the second app domain and call your current app's entry point; after your program is done unload second app domain and clean up temp files.

    (Running XslCompiledTransform in separate app domain is also important if you expect that execution of stylesheet can result in StackOverflow or OutOfMemory exceptions and want your app to be able to survive this event.)



  • Rick Kierner

    You ask correct questions.

    There are many reasons to cache compiled stylesheet and if you ca you better do this.

    Script in the styleshee is compiled to real assembly. After assembly was loaded it can be unloaded only with entire AppDomain. So when you recompile stylesheet with script again and again you leak memory.

    see: http://blogs.msdn.com/xmlteam/articles/Introducing_XslCompiledTransform.aspx

    For the same reason it's impossible for us to delete these assemblies from disk. XslCompiledTransform doesn't know when it is not used any more. And even if it would know it wouldn’t be able to delete these dlls -- they are loaded.

    So this is the task for caller app to clean temp files. To make it easier XslCompiledTransform expose TemporaryFiles property.

    see: http://msdn2.microsoft.com/en-us/library/system.xml.xsl.xslcompiledtransform.temporaryfiles(VS.80).aspx

    Another way to avoid scripts in the stylesheet is by using ExtencionObjects.

    Scripts are not evil, but have some limitations in V1 and V2 of the .Net Framework.



  • Grant McElroy

    I am working on the same issue. I also noticed that if you call Load multiple times on the same instance of the xslcompiledtransform the dispose/finalize only tries to delete the last load call. I tried to create a decorator to the load method but unfortunately the xslcompiledtransform class is sealed.

    I started to create a utility class to handle this it is just a start. Could you offer some advice as to this approach.

    using System;
    using System.IO;
    using System.Xml;
    using System.Text;
    using System.Xml.Xsl;
    using System.Xml.XPath;
    using System.Collections.Generic;
    using Microsoft.Practices.EnterpriseLibrary.Logging;

    namespace XsltDecorator
    {
    public sealed class XslCompiledTransformDecorator
    {

    #region Constructor(s)

    public XslCompiledTransformDecorator()
    {
    #if debug
    this._compiledTransform = new XslCompiledTransform(true);
    #else
    this._compiledTransform = new XslCompiledTransform(false);
    #endif
    this._realTimeDeletes = true;
    this._log = true;

    }

    public XslCompiledTransformDecorator(Boolean realTimeDeletes, Boolean log)
    {

    #if debug
    this._compiledTransform = new XslCompiledTransform(true);
    #else
    this._compiledTransform = new XslCompiledTransform(false);
    #endif
    this._realTimeDeletes = realTimeDeletes;
    this._log = log;
    }

    #endregion

    #region Public Signature(s)

    #endregion

    #region Public Signature(s)

    /// <summary>
    /// Decorated the Load method of an XslCompiled transform to try and delete any
    /// temporary files created.
    /// </summary>
    /// <param name="stylesheetUri"></param>
    public void Load(string stylesheetUri)
    {
    this.DeleteTemporaryFiles();
    this._compiledTransform.Load(stylesheetUri);
    this.LogTemporaryFiles();
    }

    /// Decorated the Load method of an XslCompiled transform to try and delete any
    /// temporary files created.
    public void Load(IXPathNavigable stylesheet)
    {
    this.DeleteTemporaryFiles();
    this._compiledTransform.Load(stylesheet);
    this.LogTemporaryFiles();
    }

    public void Load(XmlReader stylesheet)
    {
    this.DeleteTemporaryFiles();
    this._compiledTransform.Load(stylesheet);
    this.LogTemporaryFiles();
    }

    public void Load(IXPathNavigable stylesheet, XsltSettings settings, XmlResolver stylesheetResolver)
    {
    this.DeleteTemporaryFiles();
    this._compiledTransform.Load(stylesheet, settings, stylesheetResolver);
    this.LogTemporaryFiles();
    }

    public void Load(string stylesheetUri, XsltSettings settings, XmlResolver stylesheetResolver)
    {
    this.DeleteTemporaryFiles();
    this._compiledTransform.Load(stylesheetUri, settings, stylesheetResolver);
    this.LogTemporaryFiles();
    }

    public void Load(XmlReader stylesheet, XsltSettings settings, XmlResolver stylesheetResolver)
    {
    this.DeleteTemporaryFiles();
    this._compiledTransform.Load(stylesheet, settings, stylesheetResolver);
    this.LogTemporaryFiles();
    }

    //ToDo: Expose all of the .net 2.0 intrinsic Transform methods * a couple of dummy custom ones
    //ToDo: Implement the logging method

    #endregion

    #region Protected Signature(s)

    #endregion

    #region Private Signature(s)

    private Boolean _realTimeDeletes;
    private Boolean _log;
    private XslCompiledTransform _compiledTransform;

    /// <summary>
    /// Try and delete out any temporary files if they are existent
    /// </summary>
    private void DeleteTemporaryFiles()
    {
    if (_realTimeDeletes)
    {
    if ((!(this._compiledTransform.TemporaryFiles == null)) && (this._compiledTransform.TemporaryFiles.Count > 0))
    {
    //try
    //{
    this._compiledTransform.TemporaryFiles.Delete();
    //}
    //catch (IOException IOex)
    //{
    // // the real time delete failed do something. I don't know what at this point
    //}
    }
    }
    }

    private void LogTemporaryFiles()
    {
    if (_log)
    {
    //TO DO: Log all of the temporary files to a data store

    }
    }


    #endregion

    }
    }


  • Sachin Kalse

    Thanks for the quick response.

    I agree that the best practice for using the xslcompiledtransform is to minimize the Load dispatches. Currently, we lazy load all of our transforms into application variables for asp.net and cache them in class library dlls.

    What I am moving forward with is a wrapper class that contains a xslcompiledtransform instance. On the wrapped Load methods I will check if any temp files have been created already and delete them and the internal log of their creation. Then do a load and a log of the created files. On the finalize I am trying to delete out any tempfiles and then logging any files that I was unable to delete at runtime. I also exposed an unLoad boolean on all of the wrapped Transform methods to deterministically delete any temp files from the load. Then an out of process windows service will try and delete out the logged temporary files.

    I really don't see a problem with this other than losing polymorphism because the xslcompiledtransform is sealed. I am by no means trying to move forward with a practice of multiple loads but have a safeguard against the scenario happening.


  • CTA

    It shouldn't be any time you run the transformation. It should be any time you compile (Load()) it.

    This is not a bug.

    XslCompiledTransform supports some date/time rendering functions: http://msdn2.microsoft.com/en-us/library/ms256453(VS.80).aspx

    You may not need script for this.



  • mauricioe

    Here is my alpha version of my wrapper class.

    Public NotInheritable Class CompiledTransformHandler

    #Region "Constructor(s)"

    ''' <summary>

    ''' Constructor that defaults to using realtime deletes and logging is enabled

    ''' </summary>

    ''' <param name="enableDebug">true to generate debug information; otherwise false

    ''' Setting this to true enables you to debug the stylesheet with Microsoft visual studio debugger

    ''' </param>

    Public Sub New(ByVal enableDebug As Boolean)

    Me._compiledTransform = New XslCompiledTransform(enableDebug)

    Me._realTimeDeletes = True

    Me._log = True

    Me._enableDebug = enableDebug

    End Sub

    ''' <summary>

    ''' Constructor that lets you specify if you want realtimeDeletes enabled and logging

    ''' </summary>

    ''' <param name="realTimeDeletes">Turns on realtime deletes which the handler will try to delete out any temporary files</param>

    ''' <param name="log">Turns on logging which will log any remaining files that remain at the end of the object lifetime</param>

    ''' <param name="enableDebug">true to generate debug information; otherwise false

    ''' Setting this to true enables you to debug the stylesheet with Microsoft visual studio debugger

    ''' </param>

    Public Sub New(ByVal enableDebug As Boolean, ByVal realTimeDeletes As Boolean, ByVal log As Boolean)

    Me._compiledTransform = New XslCompiledTransform(enableDebug)

    Me._log = log

    Me._realTimeDeletes = realTimeDeletes

    Me._enableDebug = enableDebug

    End Sub

    #End Region

    #Region "Public Signature(s)"

    #Region "Load Signatures"

    ''' <summary>

    ''' Loads and compiles the style sheet located as a resource in the <paramref name="ptrAssembly"/>

    ''' This method encapsulates all of the logic for handling any temporary files created by the load.

    ''' </summary>

    ''' <param name="ptrAssembly">The assembly containing the style sheet</param>

    ''' <param name="name">The manifest resource name for the style sheet</param>

    ''' <param name="settings">determine the permissions for the style sheet</param>

    ''' <param name="styleSheetResolver">resolves any XSLT import or include elements</param>

    Public Sub Load(ByVal ptrAssembly As Reflection.Assembly, ByVal name As String, ByVal settings As XsltSettings, ByVal styleSheetResolver As XmlResolver)

    Using s As Stream = ptrAssembly.GetManifestResourceStream(name)

    Me.Load(New XmlTextReader(s), settings, styleSheetResolver)

    End Using

    End Sub

    ''' <summary>

    ''' Loads and compiles the style sheet located as a resource in the <paramref name="ptrAssembly"/>

    ''' This method encapsulates all of the logic for handling any temporary files created by the load.

    ''' </summary>

    ''' <param name="ptrAssembly">The assembly containing the style sheet</param>

    ''' <param name="name">The manifest resource name for the style sheet</param>

    ''' <param name="styleSheetResolver">resolves any XSLT import or include elements</param>

    Public Sub Load(ByVal ptrAssembly As Reflection.Assembly, ByVal name As String, ByVal styleSheetResolver As XmlResolver)

    Me.Load(ptrAssembly, name, New XsltSettings(), styleSheetResolver)

    End Sub

    ''' <summary>

    ''' Loads and compiles the style sheet located as a resource in the <paramref name="ptrAssembly"/>

    ''' This method encapsulates all of the logic for handling any temporary files created by the load.

    ''' </summary>

    ''' <param name="ptrAssembly">The assembly containing the style sheet</param>

    ''' <param name="name">The manifest resource name for the style sheet</param>

    ''' <param name="settings">determine the permissions for the style sheet</param>

    Public Sub Load(ByVal ptrAssembly As Reflection.Assembly, ByVal name As String, ByVal settings As XsltSettings)

    Me.Load(ptrAssembly, name, settings, New XmlUrlResolver())

    End Sub

    ''' <summary>

    ''' Loads and compiles the style sheet located as a resource in the <paramref name="ptrAssembly"/>

    ''' This method encapsulates all of the logic for handling any temporary files created by the load.

    ''' </summary>

    ''' <param name="ptrAssembly">The assembly containing the style sheet</param>

    ''' <param name="name">The manifest resource name for the style sheet</param>

    Public Sub Load(ByVal ptrAssembly As Reflection.Assembly, ByVal name As String)

    Me.Load(ptrAssembly, name, New XsltSettings(), New XmlUrlResolver())

    End Sub

    ''' <summary>

    ''' Loads and compiles the style sheet located at the specified URI.

    ''' This method encapsulates all of the logic for handling any temporary files created by the load.

    ''' </summary>

    ''' <param name="stylesheetUri">the Uri of the style sheet</param>

    Public Sub Load(ByVal styleSheetUri As String)

    Me.DeleteTemporaryFiles()

    Me._compiledTransform = New XslCompiledTransform(Me._enableDebug)

    Me._compiledTransform.Load(styleSheetUri)

    Me.LogTemporaryFiles()

    End Sub

    ''' <summary>

    ''' Compiles the style sheet contained in the IXPathNavigable object.

    ''' This method encapsulates all of the logic for handling any temporary files created by the load.

    ''' </summary>

    ''' <param name="stylesheet">stylesheet that implements IXPathNavigable</param>

    Public Sub Load(ByVal styleSheet As IXPathNavigable)

    Me.DeleteTemporaryFiles()

    Me._compiledTransform = New XslCompiledTransform(Me._enableDebug)

    Me._compiledTransform.Load(styleSheet)

    Me.LogTemporaryFiles()

    End Sub

    ''' <summary>

    ''' Compiles the style sheet contained in the XmlReader.

    ''' </summary>

    ''' <param name="stylesheet">The stylesheet that you want loaded</param>

    Public Sub Load(ByVal styleSheet As XmlReader)

    Me.DeleteTemporaryFiles()

    Me._compiledTransform = New XslCompiledTransform(Me._enableDebug)

    Me._compiledTransform.Load(styleSheet)

    Me.LogTemporaryFiles()

    End Sub

    ''' <summary>

    ''' Compiles the XSLT style sheet contained in the IXPathNavigable.

    ''' The XmlResolver resolves any XSLT import or include elements.

    ''' The XSLT settings determine the permissions for the style sheet.

    ''' </summary>

    ''' <param name="stylesheet">The stylesheet as an IXPathNavigable</param>

    ''' <param name="settings">determine the permissions for the style sheet</param>

    ''' <param name="stylesheetResolver">resolves any XSLT import or include elements</param>

    Public Sub Load(ByVal styleSheet As IXPathNavigable, ByVal settings As XsltSettings, ByVal styleSheetResolver As XmlResolver)

    Me.DeleteTemporaryFiles()

    Me._compiledTransform = New XslCompiledTransform(Me._enableDebug)

    Me._compiledTransform.Load(styleSheet, settings, styleSheetResolver)

    Me.LogTemporaryFiles()

    End Sub

    ''' <summary>

    ''' Loads and compiles the XSLT style sheet specified by the URI.

    ''' The XmlResolver resolves any XSLT import or include elements

    '''The XSLT settings determine the permissions for the style sheet.

    ''' </summary>

    ''' <param name="stylesheetUri">The uri of the stylesheet</param>

    ''' <param name="settings">determine the permissions for the style sheet</param>

    ''' <param name="stylesheetResolver">resolves any XSLT import or include elements</param>

    Public Sub Load(ByVal styleSheetUri As String, ByVal settings As XsltSettings, ByVal styleSheetResolver As XmlResolver)

    Me.DeleteTemporaryFiles()

    Me._compiledTransform = New XslCompiledTransform(Me._enableDebug)

    Me._compiledTransform.Load(styleSheetUri, settings, styleSheetResolver)

    Me.LogTemporaryFiles()

    End Sub

    ''' <summary>

    ''' Compiles the XSLT style sheet contained in the XmlReader.

    ''' The XmlResolver resolves any XSLT import or include elements

    ''' The XSLT settings determine the permissions for the style sheet.

    ''' </summary>

    ''' <param name="stylesheet">The stylesheet as an xmlreader</param>

    ''' <param name="settings">determine the permissions for the style sheet</param>

    ''' <param name="stylesheetResolver">resolves any XSLT import or include elements</param>

    Public Sub Load(ByVal styleSheet As XmlReader, ByVal settings As XsltSettings, ByVal styleSheetResolver As XmlResolver)

    Me.DeleteTemporaryFiles()

    Me._compiledTransform = New XslCompiledTransform(Me._enableDebug)

    Me._compiledTransform.Load(styleSheet, settings, styleSheetResolver)

    Me.LogTemporaryFiles()

    End Sub

    #End Region

    #Region "Transform Signatures"

    ''' <summary>

    ''' Executes the transform using the input document specified by the IXPathNavigable object and outputs the results to an XmlWriter.

    ''' </summary>

    ''' <param name="input">input document</param>

    ''' <param name="results">output results</param>

    ''' <param name="unLoad">

    ''' <para>True = unload the transform. To reuse this instance you will have to call Load again.

    ''' Use this if you don't need to reuse the transform or if this is the last call to Transform on this instance</para>

    ''' <para>False = continue to use the loaded transform</para>

    ''' </param>

    Public Sub Transform(ByVal input As IXPathNavigable, ByVal results As XmlWriter, ByVal unLoad As Boolean)

    Me._compiledTransform.Transform(input, results)

    If (unLoad) Then

    Me.DeleteTemporaryFiles()

    End If

    End Sub

    ''' <summary>

    ''' Executes the transform using the input document specified by the URI and outputs the results to a file.

    ''' </summary>

    ''' <param name="inputUri">input document specified by a URI</param>

    ''' <param name="resultsFile">output document specified by a URI</param>

    ''' <param name="unLoad">

    ''' <para>True = unload the transform. To reuse this instance you will have to call Load again.

    ''' Use this if you don't need to reuse the transform or if this is the last transformation required</para>

    ''' <para>False = continue to use the loaded transform</para>

    ''' </param>

    Public Sub Transform(ByVal inputUri As String, ByVal resultsFile As String, ByVal unLoad As Boolean)

    Me._compiledTransform.Transform(inputUri, resultsFile)

    If (unLoad) Then

    Me.DeleteTemporaryFiles()

    End If

    End Sub

    ''' <summary>

    ''' Executes the transform using the input document specified by the URI and outputs the results to an XmlWriter.

    ''' </summary>

    ''' <param name="inputUri">input document specified by a URI</param>

    ''' <param name="results">output results as an XmlWriter</param>

    ''' <param name="unLoad">

    ''' <para>True = unload the transform. To reuse this instance you will have to call Load again.

    ''' Use this if you don't need to reuse the transform or if this is the last transformation required</para>

    ''' <para>False = continue to use the loaded transform</para>

    ''' </param>

    Public Sub Transform(ByVal inputUri As String, ByVal results As XmlWriter, ByVal unLoad As Boolean)

    Me._compiledTransform.Transform(inputUri, results)

    If (unLoad) Then

    Me.DeleteTemporaryFiles()

    End If

    End Sub

    ''' <summary>

    ''' Executes the transform using the input document specified by the XmlReader object and outputs the results to an XmlWriter.

    ''' </summary>

    ''' <param name="input">input document as a XmlReader</param>

    ''' <param name="results">output results as an XmlWriter</param>

    ''' <param name="unLoad">

    ''' <para>True = unload the transform. To reuse this instance you will have to call Load again.

    ''' Use this if you don't need to reuse the transform or if this is the last transformation required</para>

    ''' <para>False = continue to use the loaded transform</para>

    ''' </param>

    Public Sub Transform(ByVal input As XmlReader, ByVal results As XmlWriter, ByVal unLoad As Boolean)

    Me._compiledTransform.Transform(input, results)

    If (unLoad) Then

    Me.DeleteTemporaryFiles()

    End If

    End Sub

    ''' <summary>

    ''' Executes the transform using the input document specified by the IXPathNavigable object and outputs the results to a stream. The XsltArgumentList provides additional runtime arguments.

    ''' </summary>

    ''' <param name="input">input document</param>

    ''' <param name="arguments">additional run-time arguments</param>

    ''' <param name="results">output results as a stream</param>

    ''' <param name="unLoad">

    ''' <para>True = unload the transform. To reuse this instance you will have to call Load again.

    ''' Use this if you don't need to reuse the transform or if this is the last transformation required</para>

    ''' <para>False = continue to use the loaded transform</para>

    ''' </param>

    Public Sub Transform(ByVal input As IXPathNavigable, ByVal arguments As XsltArgumentList, ByVal results As Stream, ByVal unLoad As Boolean)

    Me._compiledTransform.Transform(input, arguments, results)

    If (unLoad) Then

    Me.DeleteTemporaryFiles()

    End If

    End Sub

    ''' <summary>

    ''' Executes the transform using the input document specified by the IXPathNavigable object and outputs the results to an TextWriter. The XsltArgumentList provides additional run-time arguments.

    ''' </summary>

    ''' <param name="input">input document</param>

    ''' <param name="arguments">additional run-time arguments</param>

    ''' <param name="results">output results as a textWriter</param>

    ''' <param name="unLoad">

    ''' <para>True = unload the transform. To reuse this instance you will have to call Load again.

    ''' Use this if you don't need to reuse the transform or if this is the last transformation required</para>

    ''' <para>False = continue to use the loaded transform</para>

    ''' </param>

    Public Sub Transform(ByVal input As IXPathNavigable, ByVal arguments As XsltArgumentList, ByVal results As TextWriter, ByVal unLoad As Boolean)

    Me._compiledTransform.Transform(input, arguments, results)

    If (unLoad) Then

    Me.DeleteTemporaryFiles()

    End If

    End Sub

    ''' <summary>

    ''' Executes the transform using the input document specified by the IXPathNavigable object and outputs the results to an XmlWriter. The XsltArgumentList provides additional run-time arguments.

    ''' </summary>

    ''' <param name="input">input document</param>

    ''' <param name="arguments">run-time arguments</param>

    ''' <param name="results">output results as an XmlWriter</param>

    ''' <param name="unLoad">

    ''' <para>True = unload the transform. To reuse this instance you will have to call Load again.

    ''' Use this if you don't need to reuse the transform or if this is the last transformation required</para>

    ''' <para>False = continue to use the loaded transform</para>

    ''' </param>

    Public Sub Transform(ByVal input As IXPathNavigable, ByVal arguments As XsltArgumentList, ByVal results As XmlWriter, ByVal unLoad As Boolean)

    Me._compiledTransform.Transform(input, arguments, results)

    If (unLoad) Then

    Me.DeleteTemporaryFiles()

    End If

    End Sub

    ''' <summary>

    ''' Executes the transform using the input document specified by the URI and outputs the results to stream. The XsltArgumentList provides additional run-time arguments.

    ''' Use if you want the output to use the xsl:output settings

    '''</summary>

    ''' <param name="inputUri">input document&apos;s Uri</param>

    ''' <param name="arguments">run-time arguments</param>

    ''' <param name="results">output results as a stream</param>

    ''' <param name="unLoad">

    ''' <para>True = unload the transform. To reuse this instance you will have to call Load again.

    ''' Use this if you don't need to reuse the transform or if this is the last transformation required</para>

    ''' <para>False = continue to use the loaded transform</para>

    ''' </param>

    Public Sub Transform(ByVal inputUri As String, ByVal arguments As XsltArgumentList, ByVal results As Stream, ByVal unLoad As Boolean)

    Me._compiledTransform.Transform(inputUri, arguments, results)

    If (unLoad) Then

    Me.DeleteTemporaryFiles()

    End If

    End Sub

    ''' <summary>

    ''' Executes the transform using the input document specified by the URI and outputs the results to a TextWriter.

    ''' </summary>

    ''' <param name="inputUri">input document&apos;s Uri</param>

    ''' <param name="arguments">run-time arguments</param>

    ''' <param name="results">output results as a TextWriter</param>

    ''' <param name="unLoad">

    ''' <para>True = unload the transform. To reuse this instance you will have to call Load again.

    ''' Use this if you don't need to reuse the transform or if this is the last transformation required</para>

    ''' <para>False = continue to use the loaded transform</para>

    ''' </param>

    Public Sub Transform(ByVal inputUri As String, ByVal arguments As XsltArgumentList, ByVal results As TextWriter, ByVal unLoad As Boolean)

    Me._compiledTransform.Transform(inputUri, arguments, results)

    If (unLoad) Then

    Me.DeleteTemporaryFiles()

    End If

    End Sub

    ''' <summary>

    ''' Executes the transform using the input document specified by the URI and outputs the results to an XmlWriter. The XsltArgumentList provides additional run-time arguments.

    ''' </summary>

    ''' <param name="inputUri">input document&apos;s Uri</param>

    ''' <param name="arguments">run-time arguments</param>

    ''' <param name="results">output results as a XmlWriter</param>

    ''' <param name="unLoad">

    ''' <para>True = unload the transform. To reuse this instance you will have to call Load again.

    ''' Use this if you don't need to reuse the transform or if this is the last transformation required</para>

    ''' <para>False = continue to use the loaded transform</para>

    ''' </param>

    Public Sub Transform(ByVal inputUri As String, ByVal arguments As XsltArgumentList, ByVal results As XmlWriter, ByVal unLoad As Boolean)

    Me._compiledTransform.Transform(inputUri, arguments, results)

    If (unLoad) Then

    Me.DeleteTemporaryFiles()

    End If

    End Sub

    ''' <summary>

    ''' Executes the transform using the input document specified by the XmlReader object and outputs the results to a stream. The XsltArgumentList provides additional run-time arguments.

    ''' Use if you want the output to use the xsl:output settings

    ''' </summary>

    ''' <param name="input">input document as a XmlReader</param>

    ''' <param name="arguments">run-time arguments</param>

    ''' <param name="results">output document as a stream</param>

    ''' <param name="unLoad">

    ''' <para>True = unload the transform. To reuse this instance you will have to call Load again.

    ''' Use this if you don't need to reuse the transform or if this is the last transformation required</para>

    ''' <para>False = continue to use the loaded transform</para>

    ''' </param>

    Public Sub Transform(ByVal input As XmlReader, ByVal arguments As XsltArgumentList, ByVal results As Stream, ByVal unLoad As Boolean)

    Me._compiledTransform.Transform(input, arguments, results)

    If (unLoad) Then

    Me.DeleteTemporaryFiles()

    End If

    End Sub

    ''' <summary>

    ''' Executes the transform using the input document specified by the XmlReader object and outputs the results to a TextWriter. The XsltArgumentList provides additional run-time arguments.

    ''' </summary>

    ''' <param name="input">input document as a XmlReader</param>

    ''' <param name="arguments">run-time arguments</param>

    ''' <param name="results">output document as a TextWriter</param>

    ''' <param name="unLoad">

    ''' <para>True = unload the transform. To reuse this instance you will have to call Load again.

    ''' Use this if you don't need to reuse the transform or if this is the last transformation required</para>

    ''' <para>False = continue to use the loaded transform</para>

    ''' </param>

    Public Sub Transform(ByVal input As XmlReader, ByVal arguments As XsltArgumentList, ByVal results As TextWriter, ByVal unLoad As Boolean)

    Me._compiledTransform.Transform(input, arguments, results)

    If (unLoad) Then

    Me.DeleteTemporaryFiles()

    End If

    End Sub

    ''' <summary>

    ''' Executes the transform using the input document specified by the XmlReader object and outputs the results to an XmlWriter. The XsltArgumentList provides additional run-time arguments.

    ''' </summary>

    ''' <param name="input">input document as a XmlReader</param>

    ''' <param name="arguments">run-time arguments</param>

    ''' <param name="results">output results as a XmlWriter</param>

    ''' <param name="unLoad">

    ''' <para>True = unload the transform. To reuse this instance you will have to call Load again.

    ''' Use this if you don't need to reuse the transform or if this is the last transformation required</para>

    ''' <para>False = continue to use the loaded transform</para>

    ''' </param>

    Public Sub Transform(ByVal input As XmlReader, ByVal arguments As XsltArgumentList, ByVal results As XmlWriter, ByVal unLoad As Boolean)

    Me._compiledTransform.Transform(input, arguments, results)

    If (unLoad) Then

    Me.DeleteTemporaryFiles()

    End If

    End Sub

    ''' <summary>

    ''' Executes the transform using the input document specified by the XmlReader object and outputs the results to an XmlWriter.

    ''' The XsltArgumentList provides additional run-time arguments

    ''' The XmlResolver resolves the XSLT document() function.

    ''' </summary>

    ''' <param name="input">input document as a XmlReader</param>

    ''' <param name="arguments">run-time arguments</param>

    ''' <param name="results">output results as a XmlWriter</param>

    ''' <param name="documentResolver">resolves the XSLT document() function</param>

    Public Sub Transform(ByVal input As XmlReader, ByVal arguments As XsltArgumentList, ByVal results As XmlWriter, ByVal documentResolver As XmlResolver, ByVal unLoad As Boolean)

    Me._compiledTransform.Transform(input, arguments, results, documentResolver)

    If (unLoad) Then

    Me.DeleteTemporaryFiles()

    End If

    End Sub

    ''' <summary>

    ''' Finalizer that tries to delete any remaining temporary files and then persist any temp files that

    ''' need to be delete out of proc

    ''' </summary>

    ''' <remarks></remarks>

    Protected Overrides Sub Finalize()

    If (Not Me._compiledTransform Is Nothing) Then

    Me.DeleteTemporaryFiles()

    End If

    Me.PersistTemporaryFiles()

    MyBase.Finalize()

    End Sub

    #End Region

    #End Region

    #Region "Private Signature(s)"

    Private _realTimeDeletes As Boolean 'private field used for turnning on/off realtimedeletes

    Private _log As Boolean 'private field used for turnning on/off logging

    Private _compiledTransform As XslCompiledTransform 'private field used for the pointer to a compiledtransform

    Private _fileNames As New List(Of String()) 'private field used to store an array of string filenames

    Private _enableDebug As Boolean

    ''' <summary>

    ''' Delete out any temporary files if there are any

    ''' </summary>

    Private Sub DeleteTemporaryFiles()

    If (Me._realTimeDeletes) Then

    If (Not Me._compiledTransform.TemporaryFiles Is Nothing) Then

    Dim successfulDelete As Boolean = True

    Try

    Me._compiledTransform.TemporaryFiles.Delete()

    Catch ex As Exception

    successfulDelete = False

    End Try

    If (successfulDelete) Then

    If (Me._fileNames.Count > 0) Then

    Me._fileNames.RemoveAt(Me._fileNames.Count - 1) 'delete out the log for this load

    End If

    End If

    End If

    End If

    End Sub

    ''' <summary>

    ''' Logs the temporary files to an internal log to the handler

    ''' </summary>

    Private Sub LogTemporaryFiles()

    If (Me._log) Then

    Dim bd As String = Me._compiledTransform.TemporaryFiles.BasePath 'For some reason The temporaryFileCollection count doesn't register without this line

    Dim arrayLength As Integer = (Me._compiledTransform.TemporaryFiles.Count - 1)

    If (arrayLength > -1) Then

    Dim tempPaths(arrayLength) As String

    Me._compiledTransform.TemporaryFiles.CopyTo(tempPaths, 0)

    Me._fileNames.Add(tempPaths)

    End If

    End If

    End Sub

    ''' <summary>

    ''' This subroutine persists the temporary files to a db data store

    ''' </summary>

    Private Sub PersistTemporaryFiles()

    If (Me._log) Then

    If (Not Me._fileNames Is Nothing) Then

    Dim sb As New StringBuilder()

    Dim pipe As Char = "|"c

    Dim i, j, iLength, jLength As Integer

    iLength = (Me._fileNames.Count - 1)

    For i = 0 To iLength

    jLength = (Me._fileNames(i).Length - 1)

    For j = 0 To jLength

    sb.Append(Me._fileNames(i)(j))

    If (j <> jLength) Then

    sb.Append(pipe)

    Else

    If (i <> iLength) Then

    sb.Append(pipe)

    End If

    End If

    Next

    Next

    If (sb.Length > 0) Then

    Using conn As New SqlConnection(ConfigurationManager.AppSettings.Get("DB"))

    conn.Open()

    Using cmd As New SqlCommand("XslCompiledTransformHeapInsert", conn)

    cmd.CommandType = CommandType.StoredProcedure

    Dim machineParam As New SqlParameter("@MachineName", SqlDbType.VarChar)

    Dim pathParam As New SqlParameter("@Paths", SqlDbType.NVarChar)

    machineParam.Value = System.Environment.MachineName

    pathParam.Value = sb.ToString()

    cmd.Parameters.Add(machineParam)

    cmd.Parameters.Add(pathParam)

    cmd.ExecuteNonQuery()

    End Using

    End Using

    End If

    End If

    End If

    End Sub

    #End Region

    End Class


  • Fetzir

    I'd say that in most cases creating AppDomain is not necessary and over killing. So logging temp files and deleting it later is more then enough.

    Regarding reusing instance of XslCompiledTransform class.

    Loading of XSLT into XslCompuledTransfor is quite expensive. (It does a lot of work: paring text, creates AST tree; analyze it and creates several call flow graphs; analyzes them and optimizes AST based on the result; translate AST into query algebra -- QIL; optimizes QIL; converts QIL into MSIL -- managed assembly; loads assembly; JIT happens during the load and x86 code is generated).

    So it would be god practice to avoid reloading the same XSLT when this is possible.

    The same tome empty XslCompiledTransform object is very cheap. It creates couple hash tables in it and that’s all. So you will not gain any perf by reusing XslCompiledTransform.

    I expect that most of the users don't do this and most of our tests don't do this as well.

    We don't (and never had) any bugs related to reusing instances of XslCompiledTransform, but risk of finding one is higher here.

    I personally would prefer immutable objects -- objects that takes all there data in constructor and don't have any changeable state.

    XslCompiledTransform was designed to be similar to XslTransform.



  • phc

    So what do you suggest for handling the temporary files Frankley creating a new app domain just for xslt parsing is absolutely ridicoulous. I really don't see the problem with wrapping logging and deterministic deletes around your xslcompiledtransform class. Also, if you don't want people to use the same XslCompiledTransform instance with multiple loads why isn't this documented or why doesn't the class throw an exception if it has already loaded a xslt
  • Jimmy Wu

    OK, I think I'm getting really close now. The example in the documentation for the TemporaryFiles property looks like this:

    // Load the style sheet for debugging.
    XslCompiledTransform xslt = new XslCompiledTransform(true);
    xslt.Load("output.xsl");

    // Transform the file.
    xslt.Transform("books.xml", "output.xml");

    // Delete the temporary files.
    xslt.TemporaryFiles.Delete();

    But from your explanation, this won't work because the DLL is loaded and so can't be deleted. I've experimentally verified this as well -- the DLL is still there after calling Delete.

    In your blog posting that you referenced, you said, "To ensure that all temporary files will be removed, an advanced client may use the TemporaryFiles property to save paths of temporary files for a further cleanup."

    Since, as I understand it, the DLLs will be locked until my application shuts down, is the strategy to keep track of the temporary files and persist the list of them to disk, and then clean them up on the next run of the application

    Or maybe when my application is shutting down it could launch another executable that would delete the files passed to it on the command line.

    Is there another approach that you would suggest

    Thanks for your help.


  • virage87

    I'd avoid reusing the same instance of XslCompiledTransform.

    Your win will be miserable and there is a risk to find some tricky bugs, because this is not the way most people use it.



  • Lingering temporary DLLs from XSLT with script.