Note that there are some explanatory texts on larger screens.

plurals
  1. PO
    primarykey
    data
    text
    <h1>Short answer:</h1> <p>Your <code>IDatabaseService</code> interface methods return <code>XmlReader</code> objects. When you construct these, make sure to pass a <code>baseUri</code> to the constructor; e.g.:</p> <pre><code>public XmlReader GetApplicationXslt(string applicationName) { … var baseUri = string.Format("db://{0}.hist.org", applicationName); return XmlReader.Create(input: …, settings: …, baseUri: baseUri); // &lt;-- this one is important! } </code></pre> <p>If you specify this parameter, everything just might work fine. See the last section of this answer to see why I am suggesting this.</p> <hr> <h1>Long answer, introduction: Possible error sources:</h1> <p>Let's first briefly think about what component(s) could cause the error:</p> <blockquote> <p><em>"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."</em></p> </blockquote> <p>Putting stylesheets in the database means that you must have…</p> <ol> <li>changed the import paths in the stylesheets (introduced <code>db://…</code> paths)</li> <li>implemented and hooked up a custom <code>XmlDbResolver</code> for handling the <code>db://</code> import scheme</li> <li>implemented database access code in the form of <code>IDatabaseService</code>, which backs <code>XmlDbResolver</code></li> </ol> <p>If the stylesheets are unchanged except for the import paths, it would seem likely that the error is either in your <code>XmlResolver</code> class and/or in <code>IDatabaseService</code> implementation. Since you haven't shown the code for the latter, we cannot debug your code without some guessing.</p> <p>I have created a mock project using your <code>XmlDbResolver</code> (a full description follows below). <strike>I could not reproduce the error, thus I suspect that your <code>IDatabaseService</code> implementation causes the error.</strike></p> <blockquote> <p><em><strong>Update:</strong> I have been able to reproduce the error. See the OP's comment &amp; the last section of this answer.</em></p> </blockquote> <hr> <h1>My attempt to reproduce your error:</h1> <p>I've created a console application project in Visual Studio 2010 (which you can retrieve by cloning <a href="https://gist.github.com/stakx/fbbd5e7319bd6c281c50b4ebb1cee1f9" rel="nofollow noreferrer">this Gist</a> using Git (<code>git clone https://gist.github.com/fbbd5e7319bd6c281c50b4ebb1cee1f9.git</code>) and then checking out the 2nd commit, <code>git checkout d00629</code>). I'll describe each of the solution's items in more detail below.</p> <p><img src="https://i.stack.imgur.com/31eXQ.png" alt="Project items"></p> <p>(Note that the <em>Copy to output directory</em> property of <code>SqlServerDatabase.mdf</code>, <code>TestInput.xml</code>, and of both <code>.xslt</code> project items should be set to <em>Always</em>.)</p> <hr> <h2>SqlServerDatabase.mdf:</h2> <p>This is a service-based database which I'll be attaching to a local instance of SQL Server Express 2008. (This is done via the connection string in <code>App.config</code>; see below.)</p> <p>I've set up the following items inside this database:</p> <p><img src="https://i.stack.imgur.com/qcdx4.png" alt="SqlServerDatabase structure"></p> <p>This table contains two columns which are defined as follows:</p> <p><img src="https://i.stack.imgur.com/au8uw.png" alt="ApplicationDocuments column definitions"></p> <p>The tables are initially empty. Test data will be added to the database at runtime (see <code>Program.cs</code> and <code>CommonHistOrg.xslt</code> below).</p> <hr> <h2>App.config:</h2> <p>This file contains a connection string entry for the above database.</p> <pre class="lang-xml prettyprint-override"><code>&lt;?xml version="1.0"?&gt; &lt;configuration&gt; &lt;connectionStrings&gt; &lt;add name="SqlServerDatabase" connectionString="Data Source=.\SQLEXPRESS; AttachDbFilename=|DataDirectory|\SqlServerDatabase.mdf; Integrated Security=True; User Instance=True" /&gt; &lt;/connectionStrings&gt; &lt;/configuration&gt; </code></pre> <hr> <h2>IDatabaseService.cs:</h2> <p>This file contains the definition for your <code>IDatabaseService</code> interface, which I'm not repeating here.</p> <hr> <h2>SqlServerDatabaseService.cs:</h2> <p>This contains a class that implements <code>IDatabaseService</code>. It reads/writes data to the above database:</p> <pre><code>using System; using System.Collections.Generic; using System.Configuration; using System.Data.SqlClient; using System.Data.SqlTypes; using System.IO; using System.Xml; class SqlServerDatabaseService : IDatabaseService { // creates a connection based on connection string from App.config: SqlConnection CreateConnection() { return new SqlConnection(connectionString: ConfigurationManager.ConnectionStrings["SqlServerDatabase"].ConnectionString); } // stores an XML document into the 'ApplicationDocuments' table: public void StoreApplicationDocument(string applicationName, XmlReader document) { using (var connection = CreateConnection()) { SqlCommand command = connection.CreateCommand(); command.CommandText = "INSERT INTO ApplicationDocuments (ApplicationName, Document) VALUES (@applicationName, @document)"; command.Parameters.Add(new SqlParameter("@applicationName", applicationName)); command.Parameters.Add(new SqlParameter("@document", new SqlXml(document))); // ^^^^^^^^^^^^^^^^^^^^ connection.Open(); int numberOfRowsInserted = command.ExecuteNonQuery(); connection.Close(); } } // reads an XML document from the 'ApplicationDocuments' table: public XmlReader GetApplicationXslt(string applicationName) { using (var connection = CreateConnection()) { SqlCommand command = connection.CreateCommand(); command.CommandText = "SELECT Document FROM ApplicationDocuments WHERE ApplicationName = @applicationName"; command.Parameters.Add(new SqlParameter("@applicationName", applicationName)); connection.Open(); var plainXml = (string)command.ExecuteScalar(); connection.Close(); if (plainXml != null) { return XmlReader.Create(new StringReader(plainXml)); } else { throw new KeyNotFoundException(message: string.Format("Database does not contain a application document named '{0}'.", applicationName)); } } } … // (all other methods throw a NotImplementedException) } </code></pre> <hr> <h2>XmlDbResolver.cs:</h2> <p>This contains the <code>XmlDbResolver</code> class, which is identical to your <code>XmlDBResolver</code> class except for two changes:</p> <ol> <li><p>The public constructor accepts an <code>IDatabaseService</code> object. This is used instead of <code>DatabaseServiceFactory.DatabaseService</code>.</p></li> <li><p>I've had to remove the call to <code>Tracing.TraceHelper.WriteLine</code>.</p></li> </ol> <hr> <h2>CommonHistOrg.xslt:</h2> <p>This is the <code>db://common.hist.org</code> stylesheet, which will be put into the database at runtime (see <code>Program.cs</code> below):</p> <pre class="lang-xml prettyprint-override"><code>&lt;?xml version="1.0" encoding="utf-8"?&gt; &lt;xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"&gt; &lt;xsl:template match="Foo"&gt; &lt;Bar/&gt; &lt;/xsl:template&gt; &lt;/xsl:stylesheet&gt; </code></pre> <hr> <h2>TestStylesheet.xml:</h2> <p>This is a stylesheet which references the above <code>db://common.hist.org</code> stylesheet:</p> <pre><code>&lt;?xml version="1.0" encoding="utf-8"?&gt; &lt;xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"&gt; &lt;xsl:import href="db://common.hist.org"/&gt; &lt;/xsl:stylesheet&gt; </code></pre> <hr> <h2>TestInput.xml:</h2> <p>This is the XML test input document that we're going to transform using the above <code>TestStylesheet.xslt</code>:</p> <pre><code>&lt;?xml version="1.0" encoding="utf-8" ?&gt; &lt;Foo/&gt; </code></pre> <hr> <h2>Program.cs:</h2> <p>This contains the test application code:</p> <pre><code>using System; using System.Text; using System.Xml; using System.Xml.Xsl; class Program { static void Main(string[] args) { var databaseService = new SqlServerDatabaseService(); // put CommonHistOrg.xslt into the 'ApplicationDocuments' database table: databaseService.StoreApplicationDocument( applicationName: "common", document: XmlReader.Create("CommonHistOrg.xslt")); // load the XSLT stylesheet: var xslt = new XslCompiledTransform(); xslt.Load(@"TestStylesheet.xslt", settings: XsltSettings.Default, stylesheetResolver: new XmlDbResolver(databaseService)); // load the XML test input: var input = XmlReader.Create("TestInput.xml"); // transform the test input and store the result in 'output': var output = new StringBuilder(); xslt.Transform(input, XmlWriter.Create(output)); // display the transformed output: Console.WriteLine(output.ToString()); Console.ReadLine(); } } </code></pre> <p>Works like a charm on my machine: The output is an XML document with an empty root element <code>&lt;Bar/&gt;</code>, which is what the <code>db://common.hist.org</code> stylesheet outputs for the matched <code>&lt;Foo/&gt;</code> element from the test input.</p> <hr> <h1>Update: Error reproduction &amp; fix:</h1> <ol> <li><p>Insert the following statement in the <code>Main</code> method:</p> <pre><code>databaseService.StoreApplicationDocument( applicationName: "test", document: XmlReader.Create("TestStylesheet.xslt")); </code></pre></li> <li><p>Instead of</p> <pre><code>xslt.Load(@"TestStylesheet.xslt", …); </code></pre> <p>do </p> <pre><code>xslt.Load(@"db://test.hist.org", …); </code></pre> <p>This triggers the error reported by the OP.</p></li> </ol> <p>After some debugging, I have found out that the following does not cause this problem.</p> <ul> <li><p>The fact that the <code>Document</code> column in the database table has type <code>XML</code>. It fails with <code>NTEXT</code>, too.</p></li> <li><p>The fact that the <code>&lt;?xml … ?&gt;</code> header is missing from the documents that are returned from the DB. The error persists even when the XML header is manually added back before <code>SqlServerDatabaseService</code> returns control to the framework.</p></li> </ul> <p>In fact, the error is triggered somewhere in the .NET Framework code. Which is why I decided to download and install the <a href="http://referencesource.microsoft.com/" rel="nofollow noreferrer">.NET Framework reference source</a>. (I changed the solution to use version 3.5 of the framework for debugging purposes.) Installing this and restarting VS then allows you to see and step through the framework code during a debugging session.</p> <p>Starting at the call to <code>xslt.Load(…;)</code> in our <code>Main</code> method, I stepped into the framework code and eventually came to a method <code>LoadStylesheet</code> inside <code>XsltLoader.cs</code>. There's a <code>HybridDictionary</code> called <code>documentUrisInUse</code>, which apparently stores base URIs of already-loaded stylesheets. So if we load more than one stylesheet with an empty or missing base URI, this method will try to add <code>null</code> to that dictionary twice; and this is what causes the error.</p> <p>So once you assign a unique base URI to each stylesheet returned by your <code>IDatabaseService</code>, everything should work fine. You do this by passing a <code>baseUri</code> to the <code>XmlReader</code> constructor. See a code example at the very beginning of my answer. You can also retrieve an updated, working solution by <a href="https://gist.github.com/stakx/fbbd5e7319bd6c281c50b4ebb1cee1f9/archive/79efc4c57f4aeb490733c122da14b5a69fe86e8d.zip" rel="nofollow noreferrer">downloading</a> or cloning <a href="https://gist.github.com/stakx/fbbd5e7319bd6c281c50b4ebb1cee1f9" rel="nofollow noreferrer">this Gist</a> (<code>git clone https://gist.github.com/fbbd5e7319bd6c281c50b4ebb1cee1f9.git</code>).</p>
    singulars
    1. This table or related slice is empty.
    plurals
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. VO
      singulars
      1. This table or related slice is empty.
    2. VO
      singulars
      1. This table or related slice is empty.
    3. VO
      singulars
      1. This table or related slice is empty.
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload