Introducing XslCompiledTransform

This article is not finished. Please do not copy its content, make a link instead.

To improve XSLT execution performance in the .NET Framework version 2.0, the XslTransform class has been replaced with a new XSLT 1.0 implementation: the XslCompiledTransform class. XslCompiledTransform compiles XSLT stylesheets to Microsoft Intermediate Language (MSIL) methods and then executes them. Execution time of the new processor is on average 4 times better than XslTransform and matches the speed of MSXML, the native XML processor.

Although XslCompiledTransform is designed to be as compatible with XslTransform as possible, differences between the two classes nonetheless exist. The purpose of this document is to cover known differences between XslTransform to XslCompiledTransform and to simplify migration from the former to the latter.

How XslCompiledTransform Works

Before we can talk about migration, it's worth briefly outlining how XslCompiledTransform works.

Loading the Stylesheet

When the Load method is called, XslCompiledTransform reads stylesheet modules from XmlReader and creates an abstract syntax tree (AST) of the principal stylesheet module and all its imports and includes. It then compiles all script blocks to assemblies (one assembly per script namespace per script language). Finally it compiles the AST to a separate assembly.

Performing the Transformation

Once the stylesheet is fully loaded, XslCompiledTransform can transform the input document. Transformation of the input document to output involves the following steps:

  1. Parsing the input document and building an in-memory XML tree representation for it.
  2. Transforming the input XML tree to the output tree.
  3. Serialization of the output tree.

To load the cache, XslCompiledTransform uses XmlReader to read the input document. At this time, XslCompiledTransform applies whitespace stripping rules.

If the input document was specified as a Uniform Resource Identifier (URI) string, XslCompiledTransform creates an XmlReader to read the XML document from that URI using default XmlReaderSettings, but allowing Document Type Definition (DTD):

 XmlReaderSettings rs = new XmlReaderSettings();
rs.ProhibitDtd = false;
XmlReader.Create(inputUri, rs);

If the input document was specified as IXPathNavigable, XslCompiledTransform doesn't reload the cache. It calls IXPathNavigable.CreateNavigator() and uses this navigator as its cache. In this case, if the stylesheet contains xsl:strip-space or xsl:preserve-space instructions, XslCompiledTransform raises an error because it can't properly apply whitespace stripping rules.

Transformation is started by applying all template rules to the current node in the XML tree. In most cases the current node will be the root node (one that matches pattern "/"). XslCompiledTransform allows a client application to choose what node should be current at transformation start by providing input document as XPathNavigator positioned to this node. Global variables are calculated "lazily" (on first use) and the context node for them is always the root node of the input document even when the transformation has been started from a different node.

This behavior is key to allowing to rerun parts of the transformation. XslTransform, by contrast, did not support this feature. It always starts transformation from the root node.

Depending on the Transform overload called, a client application may receive transformation results in one of these forms: Stream, TextWriter, XmlWriter, or XmlReader. Internally, Transform always generates output as a set of XmlWriter events and then serializes these events to the requested form.

The stylesheet may contain one or more xsl:output instructions that specify how the transformation output should be serialized. These instructions are compiled into an instance of XmlWriterSettings class, which can be accessed via the OutputSettings property of XslCompiledTransform. A client application may receive the output tree directly by providing an instance of the XmlWriter class to the Transform method as the 'results' argument. In this case the client is responsible for output serialization. When Transform is called with Stream or TextWriter as the 'result' argument, XslCompiledTransform internally creates XmlWriter by calling XmlWriter.Create(result, outputSettings) and then uses this writer to serialize transformation output.

API Changes

Here is a WinDiff style comparison of XslTransform and XslCompiledTransform APIs:

 public sealed class XslTransform {
public sealed class XslCompiledTransform {

    // Constructors
    public XslTransform();
    public XslCompiledTransform() { }

    // New constructor allows you to debug stylesheets
    public XslCompiledTransform(bool enableDebug);

    // Properties
    public XmlResolver XmlResolver { set ; }
    // This property was not thread-safe, and thus removed from XslCompiledTransform

    public XmlWriterSettings  OutputSettings { get; }
    public TempFileCollection TemporaryFiles { get; }

    // Load stylesheet from XmlReader
    public void Load(XmlReader stylesheet);
    public void Load(XmlReader stylesheet, XmlResolver resolver);
    public void Load(XmlReader stylesheet, XmlResolver resolver, Evidence evidence);
    public void Load(XmlReader stylesheet, XsltSettings settings, XmlResolver stylesheetResolver);

    // Load stylesheet from IXPathNavigable
    public void Load(IXPathNavigable stylesheet);
    public void Load(IXPathNavigable stylesheet, XmlResolver resolver);
    public void Load(IXPathNavigable stylesheet, XmlResolver resolver, Evidence evidence);
    public void Load(IXPathNavigable stylesheet, XsltSettings settings, XmlResolver stylesheetResolver);

    // Load stylesheet from XPathNavigator
    public void Load(XPathNavigator stylesheet);
    public void Load(XPathNavigator stylesheet, XmlResolver resolver);
    public void Load(XPathNavigator stylesheet, XmlResolver resolver, Evidence evidence);
    // In .NET Framework 2.0 XPathNavigator implements IXPathNavigable, so these overloads are not needed

    // Load stylesheet from URI
    public void Load(string url);
    public void Load(string url, XmlResolver resolver);
    public void Load(string stylesheetUri);
    public void Load(string stylesheetUri, XsltSettings settings, XmlResolver stylesheetResolver);


    // Transform document read from XmlReader
    public void Transform(XmlReader input, XmlWriter results);
    public void Transform(XmlReader input, XsltArgumentList arguments, XmlWriter results);
    public void Transform(XmlReader input, XsltArgumentList arguments, TextWriter results);
    public void Transform(XmlReader input, XsltArgumentList arguments, Stream results);
    public void Transform(XmlReader input, XsltArgumentList arguments, XmlWriter results, XmlResolver documentResolver);

    // Transform IXPathNavigable to XmlReader
    public XmlReader Transform(IXPathNavigable input, XsltArgumentList args);
    public XmlReader Transform(IXPathNavigable input, XsltArgumentList args, XmlResolver resolver);
    // Not implemented in XslCompiledTransform

    // Transform IXPathNavigable to XmlWriter
    public void Transform(IXPathNavigable input, XsltArgumentList args, XmlWriter output);
    public void Transform(IXPathNavigable input, XsltArgumentList args, XmlWriter output, XmlResolver resolver);
    public void Transform(IXPathNavigable input, XmlWriter results);
    public void Transform(IXPathNavigable input, XsltArgumentList arguments, XmlWriter results);

    // Transform IXPathNavigable to TextWriter
    public void Transform(IXPathNavigable input, XsltArgumentList args, TextWriter output);
    public void Transform(IXPathNavigable input, XsltArgumentList args, TextWriter output, XmlResolver resolver);
    public void Transform(IXPathNavigable input, XsltArgumentList arguments, TextWriter results);

    // Transform IXPathNavigable to Stream
    public void Transform(IXPathNavigable input, XsltArgumentList args, Stream output);
    public void Transform(IXPathNavigable input, XsltArgumentList args, Stream output, XmlResolver resolver);
    public void Transform(IXPathNavigable input, XsltArgumentList arguments, Stream results);

    // Transform XPathNavigator
    public XmlReader Transform(XPathNavigator input, XsltArgumentList args, XmlResolver resolver);
    public XmlReader Transform(XPathNavigator input, XsltArgumentList args);
    public void Transform(XPathNavigator input, XsltArgumentList args, XmlWriter output, XmlResolver resolver);
    public void Transform(XPathNavigator input, XsltArgumentList args, XmlWriter output);
    public void Transform(XPathNavigator input, XsltArgumentList args, Stream output, XmlResolver resolver);
    public void Transform(XPathNavigator input, XsltArgumentList args, Stream output);
    public void Transform(XPathNavigator input, XsltArgumentList args, TextWriter output, XmlResolver resolver);
    public void Transform(XPathNavigator input, XsltArgumentList args, TextWriter output);
    // In .NET Framework 2.0 XPathNavigator implements IXPathNavigable, so these overloads are not needed

    // Transform document read from URI
    public void Transform(String inputfile, String outputfile);
    public void Transform(String inputfile, String outputfile, XmlResolver resolver);
    public void Transform(string inputUri, string resultsFile);
    public void Transform(string inputUri, XmlWriter results);
    public void Transform(string inputUri, XsltArgumentList arguments, XmlWriter results);
    public void Transform(string inputUri, XsltArgumentList arguments, TextWriter results);
    public void Transform(string inputUri, XsltArgumentList arguments, Stream results);
}

As you can see, the APIs of the classes are very similar. The major differences are:

  • XslCompiledTransform omits Transform overloads that return an XmlReader object.
  • Load overloads that take Evidence have been replaced by ones taking XsltSettings.
  • XslCompiledTransform introduces two new properties, OutputSettings and TemporaryFiles, and one additional constructor taking the enableDebug flag.

Let's discuss each of these in turn.

Transforming to XmlReader

The XslTransform class had several Transform overloads that return transformation results in form of an XmlReader object. These overloads were mostly used to load the transformation results into an in-memory representation (such as XmlDocument or XPathDocument) without incurring the overhead of serialization and deserialization of the resulting XML tree:

 // Load the stylesheet
XslTransform xslt = new XslTransform();
xslt.Load("MyStylesheet.xsl");

// Transform input document to XmlDocument for further processing
XmlDocument doc = new XmlDocument();
doc.Load(xslt.Transform(input, (XsltArgumentList)null));

Due to architectural and performance reasons, XslCompiledTransform does not support transforming to XmlReader. However, in .NET Framework 2.0 the XPathNavigator class introduces new methods, which allow loading an XML tree directly from XmlWriter. The same task may be accomplished now this way:

 XmlDocument doc = new XmlDocument();
using (XmlWriter writer = doc.CreateNavigator().AppendChild()) {
    xslt.Transform(input, (XsltArgumentList)null, writer);
}

Another way to obtain the transformation results in form of an XmlReader object in an efficient way is using XslReader class, which implements XmlReader by running XslCompiledTransform on a separate thread and resuming that thread each time XmlReader.Read needs a next node. XslReader code is available as a part of Mvp.Xml project.

Ensuring Security: XsltSettings Class

Instead of Evidence, XslCompiledTransform's API uses a new XsltSettings class to control stylesheet rights. This class exposes two properties, EnableScript and EnableDocumentFunction:

 public sealed class XsltSettings {
    public XsltSettings();
    public XsltSettings(bool enableDocumentFunction, bool enableScript);

    public bool EnableDocumentFunction { get; set; }
    public bool EnableScript { get; set; }

    public static XsltSettings Default {
        get { return new XsltSettings(false, false); }
    }

    public static XsltSettings TrustedXslt {
        get { return new XsltSettings(true, true); }
    }
}

Script blocks and the document function may introduce security risks and are thus disabled by default. If you trust the stylesheet and want to use those features, you need to explicitly enable them by passing an appropriately set XsltSettings object, as shown below:

 XslCompiledTransform xslt = new XslCompiledTransform();

// Disable script blocks and the document() function
// if a stylesheet came from an untrusted source
string untrustedUri = @"https://www.untrusted-site.com/meow.xsl";
XmlResolver secureResolver = new XmlSecureResolver(new XmlUrlResolver(), untrustedUri);
xslt.Load(untrustedUri, XsltSettings.Default, secureResolver);

// Enable script blocks and the document() function
// if a trusted stylesheet needs them
xslt.Load(@"C:\MyProject\purr.xsl", XsltSettings.TrustedXslt, new XmlUrlResolver());

Overloads of the Load method that do not take an XsltSettings argument, use XsltSettings.Default.

Cleanup: TemporaryFiles Property

If a stylesheet contains script blocks, or it is compiled with the enableDebug flag set to true, XslCompiledTransform may create some temporary files in the %TEMP% folder. When the finalizer of a TempFileCollection object (which keeps track of created temporary files), tries to remove all of them, some files may be in use by the current AppDomain (*.DLL files) or by the debugger (*.PDB files). Those files cannot be deleted until unloading the AppDomain or detaching the debugger. If you frequently compile XSLT stylesheets with script blocks, the %TEMP% folder may become overcrowded with temporary files left undeleted. 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.

Respecting xsl:output: OutputSettings property

If a client application uses a Transform overload that takes an XmlWriter as the output object, XslCompiledTransform ignores all xsl:output settings specified in the stylesheet. In this case the client is responsible for output serialization. It may control serialization process by either passing a custom implementation of XmlWriter or adjusting the properties of the standard XmlTextWriter. If the client needs to know serialization settings specified in the stylesheet, it may obtain them through the XslCompiledTransform.OutputSettings property. Suppose, for example, that the client needs to turn character checking off, but respect other xsl:output settings. It may use the following code to achieve that:

 // Load the stylesheet
XslCompiledTransform xslt = new XslCompiledTransform();
xslt.Load("MyStylesheet.xsl");

// Clone its output settings and turn character checking off
XmlWriterSettings ws = xslt.OutputSettings.Clone();
ws.CheckCharacters = false;

// Run the transformation with changed output settings
xslt.Transform("MyDocument.xml", XmlWriter.Create(Console.Out, ws));

Differences in Behavior

Discretionary Behaviors

For a number of error cases, the XSLT specification leaves XSLT processors flexibility to either signal an error, or recover by taking certain recovery action. The table below summarizes behaviors of XslTransform and XslCompiledTransform in such cases.

Error description Section XslTransform XslCompiledTransform
A source node matches to both xsl:strip-space and xsl:preserve-space elements, and the conflict cannot be resolved 3.4 Recover Recover
A source node matches more than one template rule, and the conflict cannot be resolved 5.5 Recover Recover
A namespace URI is declared to be an alias for multiple different namespace URIs with the same highest import precedence 7.1.1 Recover Recover
The effective value of the name attribute of xsl:element is not a valid QName 7.1.2 Error Error
The effective value of the name attribute of xsl:attribute is not a valid QName 7.1.3 Error Error
Adding an attribute to an element after children have been added to it 7.1.3 Recover Error
Creating an attribute with the name xmlns 7.1.3 Recover Error
Adding an attribute to a node that is not an element 7.1.3 Recover Error
Creating nodes other than text nodes during the instantiation of the content of the xsl:attribute element. 7.1.3 Recover Error
Two attribute set definitions with the same expanded-name and the same highest import precedence contain an attribute in common 7.1.4 Recover Recover
The effective value of the name attribute of the xsl:processing-instruction element is not both a NCName and a PITarget 7.3 Recover Error
Instantating the content of xsl:processing-instruction creates nodes other than text nodes 7.3 Recover Error
The result of instantiating the content of the xsl:processing-instruction contains the string "?>" 7.3 Recover Recover
The result of instantiating the content of the xsl:comment contains the string "--" or ends with "-" 7.4 Recover Recover
The result of instantiating the content of the xsl:comment creates nodes other than text nodes 7.4 Recover Error
The sequence of nodes created by instantiating the template within a variable-binding element contains an attribute node or a namespace node 11.2 Recover Error
There is an error retrieving the resource from the URI passed into the document function 12.1 Recover Recover/Error1
The URI reference in the document function contains a fragment identifier and there is an error processing the fragment identifier2 12.1 Recover Recover/Error1
There are multiple attributes with the same name (except for cdata-section-elements) in xsl:output with the same import precedence 16 Recover Recover
The encoding specified in the encoding attribute of the xsl:output element is not supported 16.1 Recover Recover
Disabling output escaping for a text node that is used for something other than a text node in the result tree 16.4 Recover Recover
Converting a result tree fragment to a number or string if the result tree fragment contains a text node with output escaping enabled. 16.4 Recover Recover
Output escaping is disabled for a character that is not representable in the encoding that the XSLT processor is using for output 16.4 Recover Recover
The second argument node-set to the document function is empty and the URI reference is relative3 E14 Recover Recover/Error1
The effective value of the value attribute of the xsl:number element is NaN, infinite, or less than 0.5 E24 Recover Recover
Adding a namespace node to an element after children have been added to it or after attributes have been added to it E25 Recover Error

1 Behavior depends on the document resolver passed to the Transform method. See the section devoted to the document function.

2 The default resolver, XmlUrlResolver, always ignores fragment identifiers. However, custom resolvers may regard them.

3 XmlUrlResolver will pass the relative URI reference to Path.GetFullPath and resolve it relative to the current directory. Custom resolvers may behave differently.

Loading Stylesheets and Source Documents

In XSLT both stylesheet modules and source documents are XML files. There is a lot of similarity in loading them. Here we summarize how XslCompiledTransform loads stylesheet modules and source documents and then we will see where its behavior is different from XslTransform.

XslCompiledTransform uses XmlReader to read both stylesheets and source documents. In both cases more than one XML file can be used. A stylesheet module may import or include other stylesheet modules; to load additional source documents, the document function can be used.

XslCompiledTransform delegates all work related to external URIs access to XmlResolver. To load the principal stylesheet and all includes/imports, XslCompiledTransform uses instance of XmlResolver which was passed to the Load method. To load the source document and all subsequent documents, XslCompiledTransform uses XmlResolver passed to the Transform method.

When the overload of the Load/Transform method that doesn't have XmlResolver argument is called, XmlUrlResolver is used to load subsequent stylesheets and documents. When null is passed as an XmlResolver argument to the Load/Transform method, loading of subsequent stylesheets/documents is disabled.

Whitespace Handling

A client application may pass XmlReader, URI string or IXPathNavigable to Load/Transform methods. System.Uri will be passed to XmlResolver and resolved to XmlReader, Stream or IXPathNavigable.

For subsequent stylesheets/documents XmlResolver returns to Proccessor XmlReader, Stream or IXPathNavigable.

In case when input is Stream to load XML Processor creates XmlTextReader. XmlTextReader has set of properties which control reader's behavior. (Most important properties are Normalization, ProhibitDtd, WhitespaceHandling, XmlResolver; see documentation for XmlTextReader on meaning of this properies.)

When XslCompiledTransform creates the first reader, one that is used for reading data passed directly to Load/Transform methods, it sets the reader setting the following way:

 XmlReaderSettings rs = new XmlReaderSettings();
rs.ConformanceLevel = ConformanceLevel.Document;
rs.XmlResolver = null;
rs.ProhibitDtd = true;
rs.CloseInput = true;

For each subsequent stylesheet/document XslCompiledTransform creates XmlTextReader with the same reader settings that the first XmlReader has. This guarantees consistency in loading stylesheets and source documents.

If you need to use specific reader settings in stylesheet/document load process, you have to pass XmlReader created with these settings to Load/Transform methods. If you want XmlReader for subsequent stylesheet/document document to have different reader settings, you should implement a custom XmlResolver, which returns an XmlReader with required reader settings, and pass it to a Load/Transform method.

In contrast, XslTransform always created helper XmlTextReader objects with default settings. If a client application provided the Load/Transform method with an XmlReader that has non-default reader settings, those reader settings were not regarded on subsequent document loads.

document Function

XslTransform swallowed any exceptions thrown during execution of the document function, returning an empty node-set. All possible run-time errors (e.g., network share cannot be accessed) were masked, and clients had no means to control that.

In contrast, XslCompiledTransform class does not swallow exceptions, but rethrows them wrapped in an XslTransformException. However, you can still implement XslTransform's behavior—if the GetEntity method of your custom XmlResolver returns null, it is treated as an empty node-set, and no errors will be reported.

When you used XslTransform, the GetEntity method of your XmlResolver had to return either Stream or XPathNavigator. With XslCompiledTransform, a resolver can return either Stream, XmlReader, or IXPathNavigable (and, therefore, XPathNavigator). The option of returning XmlReader is useful if you want to have precise control of loading documents, for example, to control whitespace handling, DTD handling (prohibit or allow processing a DTD), compatibility issues (use an old XmlTextReader or a new reader created by XmlReader.Create), etc.

It is common to use document('') to refer to the document node of the containing stylesheet module. Please note that the base URI of the stylesheet module must be known in order to make it work. For example, if you create an XmlReader over a stream without specifying its base URI, XSLT engine will not resolve the empty string reference correctly unless you have provided a custom XmlResolver that handles such a reference in a special way.

Extension Objects and Script Functions

Both XslTransform and XslCompiledTransform allow extending XSLT language through script blocks (ms:script elements) included in the stylesheet and extension objects passed added to XsltArgumentList. Here we outline differences between XslTransform and XslCompiledTransform in implementing these features.

Both XSLT engines use CodeDom to compile script blocks. All script blocks implementing the same namespace are concatenated and compiled into a single class. Methods of that class may be called from within XPath expressions contained in the stylesheet.

XslCompiledTransform introduces two new restrictions on use of scripts:

  • Only public methods may be called from XPath expressions.
  • Overloads are distinct only base on number of arguments. If more then one overload has the same number of arguments, an exception will be raised.

In XslCompiledTransform a binding (method name lookup) to script functions happens at compile time, and stylesheets that worked with XslTranform may cause an exception when loaded with XslCompiledTransform.

XslCompiledTransform allows the ms:script element to start with ms:using and ms:assembly child elements. The former declares an additional namespace, the latter refers an additional assembly for use in a script block.

The only difference in implementation of extension objects is that multiple overloads with the same number of arguments are prohibited now.

Extension Functions Returning a Node-Set

XslTransform allowed returning a node-set from an extension function as an object of the XPathNodeIterator class. With XslCompiledTransform you have more alternatives: a node-set may be returned as XPathNodeIterator, XPathNavigator[], or XPathNavigator (if the node-set consists of a single node).

Better Compatibility with MSXML

XslCompiledTransform makes it easier to migrate from MSXML to managed XSLT implementation by supporting some MSXML extensions. Everywhere in this section the ms prefix denotes the urn:schemas-microsoft-com:xslt namespace URI. Another frequently used prefix for this namespace URI is msxsl. Of course, you may use any other prefix in your stylesheet provided it is bound to the same URI.

ms:node-set Function

XslTransform required the argument of the ms:node-set function to be an RTF (result tree fragment), throwing an exception for an argument of any other type. Behavior of this function in XslCompiledTransform is aligned with the behavior of the same function in MSXML and exsl:node-set, which allow converting a value of an arbitrary type into a text node containing a string representation of that value. For example, ms:node-set(1 div 0) creates a text node with the value 'Infinity'.

ms:version System Property

XslTransform did not recognize the ms:version property, returning an empty string. XslCompiledTransform supports the ms:version system property. For example, system-property('ms:version') returns a string representing the version of the assembly implementing XslCompiledTransform in the same format as returned by Assembly.ImageRuntimeVersion property ('v2.0.50727' for .NET Framework 2.0).

XPath Extension Functions

MSXML provides a number of XPath extension functions to facilitate developing XSLT stylesheets. XslTransform did not support them. XslCompiledTransform supports all those functions except schema-related ones:

Function Signature and description
ms:string-compare number ms:string-compare(string x, string y[, string language[, string options]])Performs lexicographical string comparison.
ms:utc string ms:utc(string time)Converts the prefixed date/time related values into Coordinated Universal Time and into a fixed (normalized) representation that can be sorted and compared lexicographically.
ms:namespace-uri string ms:namespace-uri(string name)Resolves the prefix part of a qualified name into a namespace URI.
ms:local-name string ms:local-name(string name)Returns the local name part of a qualified name by stripping out the namespace prefix.
ms:number number ms:number(string value)Takes a string argument in XSD format and converts it into an XPath number.
ms:format-date string ms:format-date(string datetime[, string format[, string locale]])Converts standard XSD date formats to characters suitable for output.
ms:format-time string ms:format-time(string datetime[, string format[, string locale]])Converts standard XSD time formats to characters suitable for output.

The following schema-related XPath extension functions are not supported natively by XslCompiledTransform, but may be implemented as extension functions:

Function Signature and description
ms:type-is boolean ms:type-is(string URI, string local-name)Compares the current node's data type against the specified node type.
ms:type-local-name string ms:type-local-name([node-set nodes])Returns the nonqualified name of the XSD type of the current node or the first node (in document order) in the provided node-set.
ms:type-namespace-uri string ms:type-namespace-uri([node-set nodes])Returns the namespace URI associated with the XSD type of the current node or the first node (in document order) in the provided node-set.
ms:schema-info-available boolean ms:schema-info-available()Returns true if XSD information is available for the current node.
ms:script

If you migrate from MSXML, and your stylesheets contain script blocks, you will likely have to rework them, because the System.Xml assembly uses a different object model to represent nodes and node-sets than MSXML does. Moreover, managed JScript .NET and Visual Basic .NET, supported by XslCompiledTransform, differ to some extent from their native counterparts, JScript and VBScript.

In the .NET Framework 2.0, this gap is narrowed a bit. XPathNodeIterator implements the IEnumerable interface. This allows iterating node-set objects with JScript's foreach instruction.

Miscellaneous aspects

Specifying starting mode

With MSXML you can specify an initial mode, which the transformation would start in, using the setStartMode method of the IXSLProcessor interface. Both XslTransform and XslCompiledTransform do not provide means to specify a start mode, however you may usually simulate that by passing the name of the start mode as an additional parameter to your stylesheet and adding a new 'start template' containing a switch table:

 <xsl:stylesheet version="1.0" xmlns:xsl="https://www.w3.org/1999/XSL/Transform">
    <!-- Import your main stylesheet here -->
    <xsl:import href="main.xsl"/>

    <!-- Set this parameter to simulate setting a start mode -->
    <xsl:param name="start-mode" select="''"/>

    <xsl:template match="/">
        <!-- Switch mode according to the value of $start-mode -->
        <xsl:choose>
            <xsl:when test="$start-mode=''">
                <!-- Continue processing in the default mode -->
                <xsl:apply-imports/>
            </xsl:when>
            <xsl:when test="$start-mode='foo'">
                <!-- Switch to mode foo -->
                <xsl:apply-templates select="/" mode="foo"/>
            </xsl:when>
            <xsl:when test="$start-mode='bar'">
                <!-- Switch to mode bar -->
                <xsl:apply-templates select="/" mode="bar"/>
            </xsl:when>
            ...
            <xsl:otherwise>
                <xsl:message terminate="yes">Invalid start-mode</xsl:message>
            </xsl:otherwise>
        </xsl:choose>
    </xsl:template>
</xsl:stylesheet>
System.Data.SqlXml assembly

You may be surprised at seeing System.Data.SqlXml assembly on a call stack. System.Data.SqlXml is a helper assembly containing a part of XslCompiledTransform implementation. It is not supposed to be used directly, and there is no guarantee that its API will not be changed in future .NET Framework releases.

Authors: Sergey Dubinets, Anton Lapounov