Transformation XSLT avec le .NET Framework - Résoudre le cauchemar des "dynamic assemblies" !
Si vous manipulez beaucoup de fichiers XML et utilisez les transformations XSLT, vous avez dû être confronté au problème de "dynamic assemblies".
Concrètement, si votre transformation contient du script, pour chaque transformation, une dll est dynamiquement créée et chargée. Le problème est qu’il n’est pas possible de décharger une dll utilisée par un AppDomain. Nous avons donc une utilisation mémoire en constante augmentation si des transformations sont effectuées dans le cadre d’un application serveur comme un services ou un site ASP.NET.
Ce comportement est décrit dans la fiche technique suivante : "PRB: Cannot unload assemblies that you create and load by using script in XSLT" - https://support.microsoft.com/kb/316775/en-us. Il est présent quel que soit la version du .NET Framework utilisée.
En effet, décharger une assembly d’un AppDomain n’est pas possible. Nous sommes face à une restriction due à l’architecture/fonctionnement du .NET Framework. C’est pour cette raison que ce comportement est aussi présent dans le .NET Framework 4.
Voici plus de détails sur ce comportement : "Why isn't there an Assembly.Unload method?" - https://blogs.msdn.com/jasonz/archive/2004/05/31/145105.aspx
Réfléchissons sur les solutions possibles :
1. La première piste de contournement est de mettre en cache les objets de type XSLTCompiledTransform afin de les réutiliser pour les transformations ultérieures. De ce fait, nous ne créons pas de dll dynamique pour chaque transformation utilisant la même feuille de style XSLT. L’inconvénient est que cela oblige à remplir/gérer ce cache.
2. Une solution est beaucoup plus performante tant sur le plan du processeur que sur le plan de la mémoire. Elle consiste à utiliser l’utilitaire de compilation « XSLTC.EXE ». Ce dernier compile la feuille de style en deux dlls (une pour le code XSLT, une pour le code C# ou VB).
Son utilisation en ligne de commande est simple :
"C:\Program Files (x86)\Microsoft SDKs\Windows\v7.0A\Bin\xsltc.exe" /settings:script+ XSLTFile1.xslt |
Dans notre solution, nous devons ensuite référencer les dlls générées. Auparavant, le code chargeait le fichier XSLT, parsait le code javascript, compilait une dll pour chaque objet de type XslCompiledTransform. Maintenant, nous chargeons la classe qui a été précédemment compilée par XSLTC.exe.
Ancien code :
System.Xml.Xsl.XslCompiledTransform xslt = new System.Xml.Xsl.XslCompiledTransform(); xslt.Load(stylesheet, s, null); |
Nouveau :
System.Xml.Xsl.XslCompiledTransform xslt = new System.Xml.Xsl.XslCompiledTransform(); xslt.Load(typeof(XSLTFile1)); //XSLTFile1 est le nom de la classe précédemment compilé |
Au final, la dll générée par XSLTC n’est chargée qu’une fois et nous n’avons plus de compilation dynamique.
Petit détail mais qui a son importance si vous avez des centaines de feuilles de style (et donc des centaines de dlls) : Il est en effet possible d’utiliser « ILMerge.exe » pour regrouper plusieurs dlls en une même dll.
N’hésitez pas à poser un commentaire si cela vous a été utile :-)
A bientot, Sebastien.
--
Références :
- Improving XslCompiledTransform performance - https://blogs.msdn.com/spike/archive/2009/07/10/improving-xslcompiledtransform-performance.aspx
- XSLTC — Compile XSLT to .NET Assembly - https://blogs.msdn.com/antosha/archive/2007/05/28/xsltc-compile-xslt-to-.net-assembly.aspx
Comments
- Anonymous
May 14, 2010
Bonjour Sébastien, Ce n'est pas directement le sujet, mais je pense que ça peut servir dans ce genre de cadre : A noter aussi l'existence de la méthode AddExtensionObject de la classe XsltArgumentList, dont une instance peut être passée à la majorité des surcharges de la méthode XslCompiledTransform.Transform. Très utile pour :
- fournir des extensions utilisées couramment dans plusieurs transformations
- fournir des extensions riches utilisables dans des transformations dont on ne maitrise pas forcément la provenance (et pour lesquelles on n'active donc pas l'exécution de script intégré) Gaël