Microsoft KB Archive/323370

= HOW TO: Use Extension Objects When You Execute XSL Transformations in Visual C# .NET Applications =

Article ID: 323370

Article Last Modified on 1/23/2004

-

APPLIES TO


 * Microsoft .NET Framework Class Libraries 1.0
 * Microsoft .NET Framework Class Libraries 1.1

-



This article was previously published under Q323370



For a Microsoft Visual Basic .NET version of this article, see 321702.

IN THIS TASK
SUMMARY Overview of the Sample Scenario Create the Sample Extension Object Create the XSLT Style Sheet That Uses the Extension Object Create Sample ASP.NET Applications to Test the XSL Transformation
 * Supplier ASP.NET Application to Execute XSLT Transformation
 * Retailer ASP.NET Application to Query Supplier Stock Levels

Test the Sample ASP.NET Applications More Information REFERENCES



SUMMARY
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 objects.

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:       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.

This Extension 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: <ol> In Visual Studio .NET, create a new Visual C# .NET Class Library project named InventoryExtensionObject.</li> Delete the default Class1.cs module that is added to the project.</li> Add a new Class module named Products.cs to the project. Open this in the code editor, and then delete the existing code.</li>  Paste the following code in the Products.cs class module to implement the Products class: using System; using System.Data.SqlClient;

namespace InventoryExtensionObject {  public class Products {     SqlConnection cn;

public Products {        //You must modify the connection string in the following //statement to point to your instance of the Northwind SQL Server //sample database. Make sure to use a user id that has the appropriate //permissions on the database. cn = new SqlConnection(&quot;server=.;database=northwind;user id= ;password= ;&quot;); cn.Open; }

public int GetCurrentStockLevel(int ProductId) {        //Construct query to retrieve UnitsInStock for the specified ProductId. SqlCommand cmd = new SqlCommand(&quot;Select UnitsInStock from Products where&quot; +                                          &quot; ProductID = &quot; + ProductId, cn);

//Execute query to retrieve UnitsInStock. int UnitsInStock = Convert.ToInt32(cmd.ExecuteScalar); return UnitsInStock; }     public void Dispose {        cn.Close; }  } }                    </li> Save the changes to Products.cs, and then build the solution.</li></ol>

back to the top

Create the XSLT Style Sheet That Uses the Extension Object
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: <?xml version='1.0'?>

<xsl:stylesheet xmlns:xsl=&quot;http://www.w3.org/1999/XSL/Transform&quot; version=&quot;1.0&quot; xmlns:InventoryExtensionObj=&quot;urn:InventoryExtensionObj&quot;>

<xsl:template match=&quot;Products&quot;>

<HTML> <BODY> <H3>XYZ Corp. Inventory stock level</H3><BR/> </BODY> </HTML> </xsl:template>

</xsl:stylesheet> back to the top

Create the Sample ASP.NET Applications to Test the XSLT Transformation
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
<ol> In Visual Studio .NET, create a new Visual C# .NET Web Application project named SupplierSample.</li> Delete the default Web form (WebForm1.aspx) that is added to the project.</li> Add the XSLT style sheet (Products.xsl) that you created in the previous step to the Web project.</li> Add a project reference to the Extension object DLL (InventoryExtensionObject.dll) that you created in the &quot;Create the Sample Extension Object&quot; section of this article.</li> Add a new Web form named GetProductStockLevels.aspx to the project and view its code.</li>  Add the following to the end of the using directives section: using System.Xml; using System.Xml.XPath; using System.Xml.Xsl; </li>  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. XPathDocument xmlDoc = new XPathDocument(Request.InputStream); // Instantiate a new XslTransform object and load the style sheet. XslTransform xslt = new XslTransform; xslt.Load(Server.MapPath(&quot;products.xsl&quot;));

// Instantiate an XsltArgumentList object. // An XsltArgumentList object is used to supply extension object instances // and values for XSLT paarmeters required for an XSLT. transformation XsltArgumentList xsltArgList = new 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. InventoryExtensionObject.Products InventoryExtensionObj = new InventoryExtensionObject.Products; xsltArgList.AddExtensionObject(&quot;urn:InventoryExtensionObj&quot;, InventoryExtensionObj); // Set the ContentType of the ASP.NET Response object to text/html. Response.ContentType = &quot;text/html&quot;; // 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 = null; } // Exception handling code. catch(XsltCompileException xsltCompileExp) {   Response.Write(xsltCompileExp.Message); } catch(XsltException xsltExp) {   Response.Write(xsltExp.Message); } catch(XPathException XPathExp) {   Response.Write(XPathExp.Message); } catch(XmlException XmlExp) {   Response.Write(XmlExp.Message); }                   </li> Save, compile, and build the Web project solution.</li></ol>

back to the top

Retailer ASP.NET Application to Query Supplier Stock Levels
<ol> In Visual Studio .NET, create a new Visual C# .NET Web Application named RetailerSample.</li> Delete the default Web form (WebForm1.aspx) that is added to the project.</li> Add a new Web form named GetSupplierStockLevels.aspx to the project.</li> <li> 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. <%@ Page Language=&quot;cs&quot; AutoEventWireup=&quot;false&quot; Codebehind=&quot;GetSupplierStockLevels.aspx.cs&quot; Inherits=&quot;RetailerSample.GetSupplierStockLevels&quot;%> <!DOCTYPE HTML PUBLIC &quot;-//W3C//DTD HTML 4.0 Transitional//EN&quot;> <HTML> <HEAD> TestPost <meta content=&quot;Microsoft Visual Studio.NET 7.0&quot; name=&quot;GENERATOR&quot;> <meta content=&quot;Visual C# 7.0&quot; name=&quot;CODE_LANGUAGE&quot;> <meta content=&quot;JavaScript&quot; name=&quot;vs_defaultClientScript&quot;> <meta content=&quot;http://schemas.microsoft.com/intellisense/ie5&quot; name=&quot;vs_targetSchema&quot;> </HEAD> <body MS_POSITIONING=&quot;GridLayout&quot;> <form id=&quot;Form1&quot; method=&quot;post&quot; runat=&quot;server&quot;> <asp:Label id=&quot;Label1&quot; style=&quot;Z-INDEX: 101; LEFT: 39px; POSITION: absolute; TOP: 87px&quot; runat=&quot;server&quot; Width=&quot;575px&quot; Height=&quot;43px&quot; Font-Names=&quot;Arial&quot; Font-Size=&quot;Medium&quot;>Get Stock Levels from XYZ Corp. for selected Products </asp:Label> <asp:CheckBox id=&quot;Product3&quot; style=&quot;Z-INDEX: 104; LEFT: 359px; POSITION: absolute; TOP: 154px&quot; runat=&quot;server&quot; Width=&quot;170px&quot; Height=&quot;30px&quot; Font-Names=&quot;Arial&quot; Font-Size=&quot;X-Small&quot; Text=&quot;Aniseed syrup&quot;> </asp:CheckBox> <asp:CheckBox id=&quot;Product1&quot; style=&quot;Z-INDEX: 102; LEFT: 39px; POSITION: absolute; TOP: 155px&quot; runat=&quot;server&quot; Width=&quot;122px&quot; Height=&quot;35px&quot; Font-Names=&quot;Arial&quot; Font-Size=&quot;X-Small&quot; Text=&quot;Chai&quot;> </asp:CheckBox> <asp:CheckBox id=&quot;Product2&quot; style=&quot;Z-INDEX: 103; LEFT: 175px; POSITION: absolute; TOP: 155px&quot; runat=&quot;server&quot; Width=&quot;170px&quot; Height=&quot;30px&quot; Font-Names=&quot;Arial&quot; Font-Size=&quot;X-Small&quot; Text=&quot;Chang&quot;> </asp:CheckBox> <asp:Button id=&quot;Button1&quot; style=&quot;Z-INDEX: 105; LEFT: 40px; POSITION: absolute; TOP: 237px&quot; runat=&quot;server&quot; Width=&quot;134px&quot; Height=&quot;33px&quot; Text=&quot;Go&quot;> </asp:Button> <asp:Label id=&quot;Label2&quot; style=&quot;Z-INDEX: 106; LEFT: 36px; POSITION: absolute; TOP: 21px&quot; runat=&quot;server&quot; Width=&quot;579px&quot; Height=&quot;37px&quot; Font-Names=&quot;Arial&quot; Font-Size=&quot;Medium&quot; ForeColor=&quot;#0000C0&quot;>ABC Corp : Verify product stock levels with Partner company </asp:Label> </HTML> </li> <li>Open the code-behind class module of the Web form in the Visual Studio .NET code editor.</li> <li> Add the following to the end of the using directives section: using System.Net; using System.IO; using System.Text; </li> <li> 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 occured. If yes, then execute code //to generate the XML and post it to the Supplier application. if(Page.IsPostBack) {   //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. StringBuilder strXML = new System.Text.StringBuilder;

//Append a string for the root <Products> node. strXML.Append(&quot;<Products>&quot;);

//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) strXML.Append(&quot;<Product id='1' name='&quot; + Product1.Text + &quot;'/>&quot;);

if(Product2.Checked) strXML.Append(&quot;<Product id='2' name='&quot; + Product2.Text + &quot;'/>&quot;);

if(Product3.Checked) strXML.Append(&quot;<Product id='3' name='&quot; + Product3.Text + &quot;'/>&quot;);

//Append the closing </Products> node. strXML.Append(&quot;</Products>&quot;);

//Instantiate a System.Net.HttpWebRequest object to post the generated XML to the //the GetProductStockLevels.aspx web form in the Supplier application. string PostURL = &quot;http://localhost/SupplierSample/GetProductStockLevels.aspx&quot;; HttpWebRequest HttpWReq = (HttpWebRequest) WebRequest.Create(PostURL);

//Intantiate a Stream object. The generated XML will be written to this object. Stream HttpStream;

//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. 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 = &quot;POST&quot;;

//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. HttpWebResponse HttpWResp = (HttpWebResponse) HttpWReq.GetResponse;

//Use the GetResponseStream method of the HttpWebResponse object //to obtain a reference to its response stream. Stream receiveStream = HttpWResp.GetResponseStream;

//Use the GetEncoding Static method of the System.Text.Encoding object to    //instantiate a UTF-8 Encoding object. Encoding UTF8_Encoding = System.Text.Encoding.GetEncoding(&quot;utf-8&quot;);

// 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. StreamReader readStream = new StreamReader(receiveStream, UTF8_Encoding);

//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. char[] read = new char[256]; int count = readStream.Read(read, 0, 256); Console.WriteLine(&quot;HTML...\n\r&quot;); while (count > 0) {       //Write the 256 characters to a string. String str = new String(read, 0, count); Response.Write(str); count = readStream.Read(read, 0, 256); }   //Close the StreamReader. readStream.Close;

//Close the HttpWebResponse object. HttpWResp.Close; Response.End; }                   </li> <li>Save, compile, and build the Web project solution.</li></ol>

back to the top

Test the Sample ASP.NET Applications
<ol> <li> Use the following URL to locate the GetSupplierStockLevels.aspx Web form in the Retailer application: http://localhost/RetailerSample/GetSupplierStockLevels.aspx </li> <li>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.</li> <li>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.</li></ol>

back to the top

More Information
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.

Using Extension objects to implement custom routines is also a recommended solution for the problem that is described in the following Microsoft Knowledge Base article:

316775 PRB: Cannot Unload Assemblies That You Create and Load by Using Script in XSLT

back to the top

<div class="references_section">