Post Updated on Monday 8th September 2008 - See Below
This week I have been working on a server control for a site I'm working on which reads in an XML file containing some product information and then using an XSL/T file transforms the output into correctly formatted XHTML. One problem which occurred with the development of the server control was that a colleague of mine who was making modifications to the XSL/T File was unable to save the changes whilst viewing the resultant XHTML in their browser. At first hand it would appear that it would be a permissions issue, and after much checking of the permissions to confirm they were correct, it was determined this was not the issue.
My colleague had been able to save changes to the XSL/T after a short while of the page processing which immediately pointed me to the fact that the object was still holding onto the resources. This was further confirmed with some investigation work which I did with the help of Craig Murphy.
The XMLReader object which was reading and transforming the XML using the XSL/T was still holding on to the file and placing a lock on it because the XMLReader object doesn't have a dispose method and so therefore despite my setting the object to nothing, until the garbage collector picked up the objects then the lock on the XSL/T file remained. Craig suggested I call the XMLReader.Close() method after I had finished processing the XML, this was a method I wasn't aware of and had not seen used in any example when I did my research into the changed syntax since version 2.0 of the .Net Framework.
Here is a code snippet below of an example of how to use the XMLReader.Close() method to release the locks on resources:
Dim strProductDataXML As String = "ProductData.xml"
Dim strProductDataXSLT As String = "ProductDataTransform.xsl"
Dim sb As New StringBuilder()
Dim sw As New StringWriter(sb)
' Create XSL CompiledTransform to hold XSLT
Dim objXSL As New XslCompiledTransform
' Create XsltSettings to hold XSLT Settings
Dim objXSLTSettings As New XsltSettings(False, True)
' Create XmlReader Object to read the XSLT
Dim objXSLReader As XmlReader
' Create XMLReaderSettings Object For XMLReader Object which will read the XSLT
Dim objXSLReaderSettings As New XmlReaderSettings()
' Create XMLSecureResolver for use with the XSLCompiledTransform
Dim objXMLResolver As New XmlSecureResolver(New XmlUrlResolver(), "http://www.mysite.com/")
'Create Reader Object to read the XSL Transform
objXSLReader = XmlReader.Create(HttpContext.Current.Server.MapPath("~/XSLT/ProductDataTransform.xsl"), objXSLReaderSettings)
' Load the Transform in the XSLCompiledTransform Object, Provide the settings and resolver objects
objXSL.Load(objXSLReader, objXSLTSettings, objXMLResolver)
' Set the Source of the ProductData XML File to be transformed
Dim xmlSource As New XmlTextReader(HttpContext.Current.Server.MapPath("~/XML/ProductData.xml"))
Dim xPathDoc As New XPathDocument(xmlSource)
' Call the close method on the XMLTextReader to release all resources which is attached too.
xmlSource.Close()
' Perform the Transformation and output it to a stringwriter object
objXSL.Transform(xPathDoc, Nothing, sw)
' Call the close method on the XSLReader to release all resources which is attached too.
objXSLReader.Close()
' Set all objects to Nothing to allow the Garbage Collector to remove them
strProductDataXML = Nothing
strProductDataXSLT = Nothing
objXSL = Nothing
objXSLTSettings = Nothing
objXSLReader = Nothing
objXSLReaderSettings = Nothing
objXMLResolver = Nothing
xmlSource = NothingxPathDoc = Nothing
' Return the transformed string
Return sb.ToString()
Now that the XMLReader is closed and has released all of the objects holding on to it, the XSL/T file can be edited and changed without having to wait for the Garbage Collector to remove the objects and release the locks.
UPDATE - After reading the comments I have received on this post, I have tidied up the code and made use of the Using structure, to be honest I had forgotten this was available in VB.Net from version 2.0 of the Framework. The use of the Using structure handles the disposal of the objects and therefore releases the locks on the resources the objects are using. Below is the updated code sample:
Dim strProductDataXML As String = "ProductData.xml"
Dim strProductDataXSLT As String = "ProductDataTransform.xsl"
Dim sb As New StringBuilder()
' Create XSL CompiledTransform to hold XSLT
Dim objXSL As New XslCompiledTransform
' Create XsltSettings to hold XSLT Settings
Dim objXSLTSettings As New XsltSettings(False, True)
' Create XMLReaderSettings Object For XMLReader Object which will read the XSLT
Dim objXSLReaderSettings As New XmlReaderSettings()
' Create XMLSecureResolver for use with the XSLCompiledTransform
Dim objXMLResolver As New XmlSecureResolver(New XmlUrlResolver(), "http://www.mysite.com")
objXSLReaderSettings.ProhibitDtd = False
'Create objXSL XMLReader Object to read the XSL Transform
Using objXSLReader As XmlReader = XmlReader.Create(HttpContext.Current.Server.MapPath("~/XSLT/" & strProductDataXSLT), objXSLReaderSettings)
' Load the Transform into the XSLCompiledTransform Object, Provide the settings and resolver objects
objXSL.Load(objXSLReader, objXSLTSettings, objXMLResolver)
' Set the Source of the ProductData XML File to be transformed
Using xmlSource As New XmlTextReader(HttpContext.Current.Server.MapPath("~/XML/" & strProductDataXML))
Dim xPathDoc As New XPathDocument(xmlSource)
Using sw As New StringWriter(sb)
' Perform the transformation and output it to a stringwriter object
objXSL.Transform(xPathDoc, Nothing, sw)
End Using
End Using
End Using
Return sb.ToString()
I'm sure you'll agree it's a lot cleaner and better structured.
13fd3f75-ab02-46c0-8e5e-06ac2ee685dc|0|.0|96d5b379-7e1d-4dac-a6ba-1e50db561b04