In-line script blocks and external code components can be used to implement custom routines that are invoked during the course of an XSL transformation (XSLT) to perform calculations and process data. Microsoft recommends that you avoid the usage of in-line script blocks, and instead use external code components to implement such custom routines when you design portable XSLT style sheets. External code components that are used by XSLT are referred to as XSLT Extension
This step-by-step article describes how to set up and test sample ASP.NET applications based on a possible real world scenario to demonstrate how to use Extension
objects while you execute XSLT in Microsoft .NET applications.back to the top Overview of the Sample Scenario
This section provides a description of the scenario that drives the design of the sample application components that you will create.
ABC Corp. is a retailer that stocks and sells food supplies that are manufactured by a few select suppliers. ABC Corp. uses an ASP.NET Intranet application to record and track information that pertains to its inventory, customer orders, and external supplier purchase orders. One limitation in the current inventory management and purchase order modules is the lack of a feature to dynamically view the current stock levels for a product at a supplier site before placing a purchase order. The information technology team at ABC Corp. has informed the management that this feature can be implemented if the information systems that are used by the suppliers have provisions to dynamically exchange the required data. The management team at ABC Corp. discussed this functionality with each of the suppliers and found that none of the suppliers had the provision to handle this requirement.
One of the suppliers, XYZ Corp., expressed interest in pursuing this option because it felt that such a feature can also benefit other retailers that XYZ Corp. does business with. The information systems at XYZ Corp. are also built on the .NET Framework by using .NET technologies. The management teams of both the organizations decided to let their information technology personnel meet and brainstorm the implementation architecture for this feature. The following is a high-level listing of the components of the final agreed-upon implementation model:
- The retailer (ABC Corp.) wants to view the current stock levels for selected products at the supplier site (XYZ Corp.) formatted and displayed as an HTML table.
- The retailer will use XML to send the list of product IDs for which current stock levels are required from the supplier. The XML will be constructed based on the products that are selected from a list that is displayed on an ASP.NET Web form. The generated XML will have the same format as the following sample:
<?xml version="1.0"?><Products> <Product id="1" name="Chai"/> <Product id="2" name="Chang"/> </Products>
- The supplier will use an XSLT style sheet to transform the received XML and generate the HTML. The XSLT style sheet will use the XSLT Extension object to dynamically query the supplier Inventory database and include data about the product stock levels in the generated HTML.
The rest of this article describes how to implement the components that are described in this model. To keep things simple, you will create and test all of the components on a single computer. back to the top Create the Sample Extension Object
In this section you create the Extension
object that the supplier (XYZ Corp.) uses in the sample scenario to dynamically retrieve product stock information. For illustration purposes, this sample uses the data that is contained in the Products table in the SQL Server Northwind sample database. The Extension
object implements a single method named GetCurrentStockLevel
. The method takes a product ID as its input parameter and returns the current units in stock for the specified product based on the data in the Products table.
object and method are invoked by the XSLT style sheet that is used to transform the XML that is posted by the retailer (ABC Corp.) to retrieve current stock information for the requested products.
To create this component:
back to the top Create the XSLT Style Sheet That Uses the Extension Object
- In Visual Studio .NET, create a new Visual Basic .NET Class Library project named InventoryExtensionObject.
- Delete the default Class1.vb module that is added to the project.
- Add a new Class module named Products.vb to the project. Open this in the code editor, and then delete the existing code.
- Paste the following code in the Products.vb class module to implement the Products class:
Note You must change the User ID <username> value and the password =<strong password> value to the correct values before you run this code. Make sure that User ID has the appropriate permissions to perform this operation on the database.
Imports System.Data.SqlClientPublic Class Products Private cn As SqlConnection Public Function GetCurrentStockLevel(ByVal ProductId As Integer) As Integer 'Construct query to retrieve UnitsInStock for the specified ProductId. Dim cmd As New SqlCommand("Select UnitsInStock from Products where ProductID = " & ProductId, cn) Dim UnitsInStock As Integer 'Execute query to retrieve UnitsInStock. UnitsInStock = CType(cmd.ExecuteScalar(), Integer) Return UnitsInStock End Function Public Sub New() 'You will need to modify the connection string in the following 'statement to point to your instance of the Northwind SQL Server 'sample database. cn = New SqlConnection("server=.;database=northwind;user id=<username>;password=<strong password>;") cn.Open() End Sub Public Sub Dispose() cn.Close() End SubEnd Class
- Save the changes to Products.vb, and then build the solution.
In this section you create the XSLT style sheet that the supplier (XYZ Corp.) uses in the sample scenario to generate the HTML table that displays information about the current stock level for products that the retailer (ABC Corp.) requests. This style sheet is used to transform the XML that is posted by the retailer that contains the list of product IDs for which current stock information is required. It uses the Extension
object that you created in the previous section to retrieve data that pertains to the current units in stock for each product.
Use the following code to create and save an XSLT style sheet named Products.xsl to your hard disk:
back to the top Create the Sample ASP.NET Applications to Test the XSLT Transformation
<?xml version='1.0'?><!-- Notice how a namespace URI and prefix are declared to reference the extension object --> <xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0" xmlns:InventoryExtensionObj="urn:InventoryExtensionObj"><xsl:template match="Products"> <HTML> <BODY> <H3>XYZ Corp. Inventory stock level</H3><BR/> <TABLE BORDER="1"> <TR> <TH>Product Id</TH> <TH>Product Name</TH> <TH>Units In Stock</TH> </TR> <xsl:for-each select="Product"> <TR> <TD><xsl:value-of select="@id"/></TD> <TD><xsl:value-of select="@name"/></TD> <!-- Call extension object method to retrieve stock information. Notice how the id attribute of the Product element is passed as the input parameter. The actual instance of the Extension object will be supplied by the code that executes the XSLT transformation. --> <TD><xsl:value-of select="InventoryExtensionObj:GetCurrentStockLevel(@id)"/></TD> </TR> </xsl:for-each> </TABLE> </BODY> </HTML> </xsl:template> </xsl:stylesheet>
In this section you create two sample ASP.NET applications. One ASP.NET application is used to host an ASP.NET Web form that the retailer (ABC Corp.) uses in the sample scenario to select the products for which updated stock information is required. This Web form contains code in its code-behind class module to generate and post the XML that contains the list of selected products to a component on the site of the supplier (XYZ Corp.) and to display the returned HTML response.
The other ASP.NET application is used to implement a Web form in the system of the supplier to which the XML that the retailer generates is posted. The code-behind class module of this Web form contains code to load the XML that is posted to it and executes the XSLT transformation by using the XSLT style sheet that you created in the previous step to generate and return the required HTML. It also contains the code to create and supply an instance of the Extension
object to the XSLT transformation process. First you create the ASP.NET application for the supplier. back to the top
Supplier ASP.NET Application to Execute XSLT Transformation
back to the top
- In Visual Studio .NET, create a new Visual Basic .NET Web Application project named SupplierSample.
- Delete the default Web form (WebForm1.aspx) that is added to the project.
- Add the XSLT style sheet (Products.xsl) that you created in the previous step to the Web project.
- Add a project reference to the Extension object DLL (InventoryExtensionObject.dll) that you created in the "Create the Sample Extension Object" section of this article.
- Add a new Web form named GetProductStockLevels.aspx to the project and view its code.
- Add the following Imports statements at the top of the of the class module before the Class declaration:
Imports System.XmlImports System.Xml.XPathImports System.Xml.Xsl
- Paste the following code in the Page_Load event procedure to execute the XSLT transformation on the XML that is posted to the Web form. Study the inline comments to see how the Extension object is instantiated and used by the transformation process:
Try 'Instantiate a new XPathDocument object and load the posted XML. Dim xmldoc As New XPathDocument(Request.InputStream) 'Instantiate a new XslTransform object and load the style sheet. Dim xslt As New XslTransform() xslt.Load(Server.MapPath("products.xsl")) 'Instantiate an XsltArgumentList object. 'An XsltArgumentList object is used to supply extension object instances 'and values for XSLT paarmeters required for an XSLT transformation. Dim xsltArgList As New System.Xml.Xsl.XsltArgumentList() 'Instantiate and add an instance of the extension object to the XsltArgumentList. 'The AddExtensionObject method is used to add the Extension object instance to the 'XsltArgumentList object. The namespace URI specified as the first parameter 'should match the namespace URI used to reference the Extension object in the 'XSLT style sheet. Dim InventoryExtensionObj As New InventoryExtensionObject.Products() xsltArgList.AddExtensionObject("urn:InventoryExtensionObj", InventoryExtensionObj) 'Set the ContentType of the ASP.NET Response object to text/html. Response.ContentType = "text/html" 'Execute the transformation and generate the output to the Response object's 'output stream. Notice how the XsltArgumentList object to which the Extension 'object instance was added is supplied as a parameter when executing the 'Transform method of the XslTransform object. xslt.Transform(xmlDoc, xsltArgList, Response.OutputStream) InventoryExtensionObj.Dispose() InventoryExtensionObj = Nothing 'Exception handling code. Catch xsltExp As System.Xml.Xsl.XsltException Response.Write(xsltExp.Message) Catch xsltCompileExp As System.Xml.Xsl.XsltCompileException Response.Write(xsltCompileExp.Message) Catch XPathExp As System.Xml.XPath.XPathException Response.Write(XPathExp.Message) Catch XmlExp As XmlException Response.Write(XmlExp.Message)End Try
- Save, compile, and build the Web project solution.
Retailer ASP.NET Application to Query Supplier Stock Levels
back to the top
- In Visual Studio .NET, create a new Visual Basic .NET Web Application named RetailerSample.
- Delete the default Web form (WebForm1.aspx) that is added to the project.
- Add a new Web form named GetSupplierStockLevels.aspx to the project.
- Switch to the HTML pane of the Web form in the designer window and replace the existing code with the following to generate a simple test user interface: The user interface displays three check boxes that represent three products that are supplied by the supplier (and for which data exists in the Northwind sample database) in the sample scenario. The check boxes are hard coded in this sample for simplicity. They can be generated dynamically by using data binding and the ASP.NET CheckBoxList server control in a real world application.
- Open the code-behind class module of the Web form in the Visual Studio .NET code editor.
- Add the following Imports statements before the GetSupplierStockLevels class declaration:
Imports System.NetImports System.IO
- Paste the following code in the Page_Load event procedure: This code generates the XML listing the selected products, posts the XML to the supplier application, and displays the returned HTML to generate the table that lists the current supplier stock levels. It uses the HttpWebRequest and HttpWebResponse classes in the System.Net namespace to post the XML to the supplier site and access the returned response. For more information about the methods of these objects used in this code, see the .NET Framework SDK documentation about these classes.
'Check to see if a post back occurred. If yes, then execute code 'to generate the XML and post it to the Supplier application. If Page.IsPostBack Then 'Use a StringBuilder object to generate the XML string to post to the 'Supplier application. Strings in .NET are non-mutable. Using a String 'variable to generate the following XML string would result in multiple 'String objects being created behind the scenes each time the variable is 'modified. This can be prevented by using a StringBuilder object. Dim strXML As New System.Text.StringBuilder() 'Append a string for the root <Products> node. strXML.Append("<Products>") 'Generate a <Product> node for each selected product. To do this, 'examine the Checked property of each product check box on the web form. If Product1.Checked = True Then strXML.Append("<Product id='1' name='" & Product1.Text & "'/>") End If If Product2.Checked = True Then strXML.Append("<Product id='2' name='" & Product2.Text & "'/>") End If If Product3.Checked = True Then strXML.Append("<Product id='3' name='" & Product3.Text & "'/>") End If 'Append the closing </Products> node. strXML.Append("</Products>") 'Instantiate a System.Net.HttpWebRequest object to post the generated XML to the 'the GetProductStockLevels.aspx web form in the Supplier application. Dim PostURL as String = "http://localhost/SupplierSample/GetProductStockLevels.aspx" Dim HttpWReq As HttpWebRequest = CType(WebRequest.Create(PostURL), HttpWebRequest) 'Intantiate a Stream object. The generated XML will be written to this object. Dim HttpStream As Stream 'Instantiate a Byte Array and use the GetBytes Static method of 'of the System.Text.Encoding.UTF8 class to write the UTF-8 encoded 'byte representation of the generated XML string to the Byte Array. Dim buf As Byte() buf = System.Text.Encoding.UTF8.GetBytes(strXML.ToString) 'Set the Method property of the HttpWebRequest to POST to indicate 'that it will be used to execute an HTTP POST request. HttpWReq.Method = "POST" 'Write the contents of the Byte Array (the UTF-8 encoded byte representation 'of the XML string) to the HttpWebRequest's RequestStream. This is the data that 'will be posted to the target URL. The GetRequestStream method is used to obtain 'a reference to the HttpWebRequest's request stream. HttpStream = HttpWReq.GetRequestStream() HttpStream.Write(buf, 0, buf.Length) HttpStream.Close() 'Execute the GetResponse method of the HttpWebRequest object to execute the POST 'and use the generated Response to instantiate an HttpWebResponse object. Dim HttpWResp As HttpWebResponse = CType(HttpWReq.GetResponse(), HttpWebResponse) 'Use the GetResponseStream method of the HttpWebResponse object 'to obtain a reference to its response stream. Dim receiveStream As Stream = HttpWResp.GetResponseStream() 'Use the GetEncoding Static method of the System.Text.Encoding object to 'instantiate a UTF-8 Encoding object. Dim encode As System.Text.Encoding = System.Text.Encoding.GetEncoding("utf-8") ' Pipe the response stream to a higher level stream reader with the required 'encoding format. The StreamReader object will be used to access the contents 'of the ResponseStream. Dim readStream As New StreamReader(receiveStream, encode) 'Loop through and write out the contents of the ResponseStream (contains 'the HTML generated and returned by the Supplier application) 256 characters 'at a time to the ASP.NET Response object to display the HTML table listing 'the Products and their current stock levels at the Supplier site. Dim read(256) As Char Dim count As Integer = readStream.Read(read, 0, 256) Console.WriteLine("HTML..." + ControlChars.Lf + ControlChars.Cr) While count > 0 'Write the 256 characters to a string. Dim str As New String(read, 0, count) Response.Write(str) count = readStream.Read(read, 0, 256) End While 'Close the StreamReader. readStream.Close() 'Close the HttpWebResponse object. HttpWResp.Close() Response.End() End If
- Save, compile, and build the Web project solution.
Test the Sample ASP.NET Applications
back to the top
- Use the following URL to locate the GetSupplierStockLevels.aspx Web form in the Retailer application:
- Use the check boxes on the Web form to select the products for which you want to obtain current stock information from the Supplier application.
- Click the Go button to generate the XML to reflect the selected products, post the XML to the GetProductStockLevels.aspx Web form in the Supplier application, and display the returned HTML. You will see a HTML table that lists the selected products together with their current stock levels at the Supplier site.
Implementing and accessing Extension
objects in XSLT style sheets is the recommended design methodology when you must invoke custom routines in an XSL transformation. XSLT Extension
objects do not require inline script blocks in the XSLT style sheet. Inline script blocks may make the style sheet dependent on vendor-specific XSLT-extension script tags and scripting or programming language interpreters or compilers. XSLT Extension
objects, on the other hand, do not specify or define any implementation details that are related to the technologies and components that are used to package the custom routines. All that is required in the XSLT style sheet is a namespace URI and prefix that are associated with the Extension
object. The defined prefix is used to qualify calls that are made to the methods of the Extension
object in the XSLT style sheet. You can use the technology of your choice to implement the actual Extension
object component. The only requirement is that the XML or XSLT processor that is used to execute the transformation must implement an interface that permits you to supply instances of Extension
objects as input arguments to an XSLT transformation process.
objects to implement custom routines is also a recommended solution for the problem that is described in the following Microsoft Knowledge Base article:
316775back to the top
PRB: Cannot Unload Assemblies That You Create and Load by Using Script in XSLT