Is there a way to debug XSLT documents that are loaded from a database by a custom XmlUrlResolver or does anyone know, what the errormessage below is about?
I have a XSLT stylesheet that imports a common xslt document:
<xsl:import href="db://common.hist.org"/>
The Scheme is handled by a custom XmlResolver
that loads the XSLT document from a DB, but I get an error:
An entry with the same key already exists.
The common XSLT document referred to by the xsl:import
contains some common XSLT templates, each with a unique name.
This error began to occur after having moved the XSLT documents from the local file system to the database. When using default import schemes pointing to local files and when loading the XSLT documents from the local filesystem, the error does not occur.
I also tried to turn on debugging when creating the instance of the XslCompiledTransform
, but somehow it is not possible to "step into" the database-based XSLT.
_xslHtmlOutput = new XslCompiledTransform(XSLT_DEBUG);
Update: The following is basically the resolver code as requested, but the exception is not happening inside my code; thus I guess no obvious reason in this code below. (This same code is actually used to load the XSLT stylesheets that contain the imports, and when commenting out the imports everything works as expected.)
public class XmlDBResolver : XmlUrlResolver
{
private IDictionary<string,string> GetUriComponents(String uri)
{
bool useXmlPre = false;
uri = uri.Replace("db://", "");
useXmlPre = uri.StartsWith("xml/");
uri = uri.Replace("xml/", "");
IDictionary<string, string> dict = new Dictionary<string, string>();
string app = null, area = null, subArea = null;
if (!String.IsNullOrWhiteSpace(uri))
{
string[] components = uri.Split('.');
if (components == null)
throw new Exception("Invalid Xslt URI");
switch (components.Count())
{
case 3:
app = components[0];
break;
case 4:
area = components[0];
app = components[1];
break;
case 5:
subArea = components[0];
area = components[1];
app = components[2];
break;
default:
throw new Exception("Invalid Xslt URI");
}
dict.Add("application", app);
dict.Add("area", area);
dict.Add("subArea", subArea);
dict.Add("xmlPreTransform", String.Format("{0}", useXmlPre));
}
return dict;
}
public override System.Net.ICredentials Credentials
{
set { /* TODO: check if we need credentials */ }
}
public override object GetEntity(Uri absoluteUri, string role, Type ofObjectToReturn)
{
/*
* db://<app>.hist.org
* db://<area>.<app>.hist.org
* db://<subArea>.<area>.<app>.hist.org
*
* */
Tracing.TraceHelper.WriteLine(String.Format("GetEntity {0}", absoluteUri));
XmlReader reader = null;
switch (absoluteUri.Scheme)
{
case "db":
string origString = absoluteUri.OriginalString;
IDictionary<string, string> xsltDict = GetUriComponents(origString);
if(String.IsNullOrWhiteSpace(xsltDict["area"]))
{
reader = DatabaseServiceFactory.DatabaseService.GetApplicationXslt(xsltDict["application"]);
}
else if (!String.IsNullOrWhiteSpace(xsltDict["area"]) && String.IsNullOrWhiteSpace(xsltDict["subArea"]) && !Boolean.Parse(xsltDict["xmlPreTransform"]))
{
reader = DatabaseServiceFactory.DatabaseService.GetAreaXslt(xsltDict["application"], xsltDict["area"]);
}
else if (!String.IsNullOrWhiteSpace(xsltDict["area"]) && !String.IsNullOrWhiteSpace(xsltDict["subArea"]))
{
if(Boolean.Parse(xsltDict["xmlPreTransform"]))
reader = DatabaseServiceFactory.DatabaseService.GetSubareaXmlPreTransformXslt(xsltDict["application"], xsltDict["area"], xsltDict["subArea"]);
else
reader = DatabaseServiceFactory.DatabaseService.GetSubareaXslt(xsltDict["application"], xsltDict["area"], xsltDict["subArea"]);
}
return reader;
default:
return base.GetEntity(absoluteUri, role, ofObjectToReturn);
}
}
and for completeness the IDatabaseService interface (relevant parts):
public interface IDatabaseService
{
...
XmlReader GetApplicationXslt(String applicationName);
XmlReader GetAreaXslt(String applicationName, String areaName);
XmlReader GetSubareaXslt(String applicationName, String areaName, String subAreaName);
XmlReader GetSubareaXmlPreTransformXslt(String applicationName, String areaName, String subAreaName);
}
Update: I tried to isolate the problem by temporarily loading the stylesheets from a web server instead, which works. I learned that the SQL Server apparently stores only XML fragments without the XML declaration, in contrast to the stylesheets being stored on a webserver.
Update: The stacktrace of the Exception:
System.Xml.Xsl.XslLoadException: XSLT-Kompilierungsfehler. Fehler bei (9,1616). ---> System.ArgumentException: An entry with the same key already exists.. bei System.Collections.Specialized.ListDictionary.Add(Object key, Object value) bei System.Collections.Specialized.HybridDictionary.Add(Object key, Object value) bei System.Xml.Xsl.Xslt.XsltLoader.LoadStylesheet(XmlReader reader, Boolean include) bei System.Xml.Xsl.Xslt.XsltLoader.LoadStylesheet(Uri uri, Boolean include) bei System.Xml.Xsl.Xslt.XsltLoader.LoadStylesheet(XmlReader reader, Boolean include) --- Ende der inneren Ablaufverfolgung des Ausnahmestacks --- bei System.Xml.Xsl.Xslt.XsltLoader.LoadStylesheet(XmlReader reader, Boolean include) bei System.Xml.Xsl.Xslt.XsltLoader.Load(XmlReader reader) bei System.Xml.Xsl.Xslt.XsltLoader.Load(Compiler compiler, Object stylesheet, XmlResolver xmlResolver) bei System.Xml.Xsl.Xslt.Compiler.Compile(Object stylesheet, XmlResolver xmlResolver, QilExpression& qil) bei System.Xml.Xsl.XslCompiledTransform.LoadInternal(Object stylesheet, XsltSettings settings, XmlResolver stylesheetResolver) bei System.Xml.Xsl.XslCompiledTransform.Load(String stylesheetUri, XsltSettings settings, XmlResolver stylesheetResolver) bei (my namespace and class).GetXslTransform(Boolean preTransform) bei (my namespace and class).get_XslHtmlOutput() bei (my namespace and class).get_DisplayMarkup()
GetUriComponents(String uri)
but you haven't shown the code of this method. To verify this, try filling manually the desired dictionary items -- and not calling this method. It is likely you won't get that error message in this case. – Dimitre Novatchev