XslCompiledTransform and custom XmlUrlResolver: "A record with the same key already exists"

Is there a way to debug XSLT documents that are loaded from the database by a custom XmlUrlResolver or does anyone know what the errors are about?

I have an XSLT stylesheet that imports a generic xslt document:

<xsl:import href="db://common.hist.org"/>

This schema is handled by a user XmlResolverthat loads an XSLT document from a database, but I get an error message:

A record with the same key already exists.

The referenced general XSLT document xsl:importcontains some common XSLT templates, each with a unique name.

This error started to occur after moving XSLT documents from the local file system to the database. When using default import schemes that point to local files, and when loading XSLT documents from the local file system, an error does not occur.

I also tried to enable debugging when creating the instance XslCompiledTransform, but somehow it is impossible to "enter" into the database-based XSLT.

_xslHtmlOutput = new XslCompiledTransform(XSLT_DEBUG);

Update: The following is essentially the resolver code, but the exception does not occur inside my code; therefore, I see no obvious reason in this code. (The same code is actually used to load XSLT stylesheets containing the import, and when commenting on the import 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 of the IDatabaseService interface (corresponding 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);
}

: , -. , SQL Server, -, XML XML, , -.

: :

System.Xml.Xsl.XslLoadException: XSLT-Kompilierungsfehler. Fehler bei (9,1616). --- > System.ArgumentException: .. bei System.Collections.Specialized.ListDictionary.Add( , ) 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( , , XmlResolver xmlResolver) bei System.Xml.Xsl.Xslt.Compiler.Compile( , XmlResolver xmlResolver, QilExpression & qil ) bei System.Xml.Xsl.XslCompiledTransform.LoadInternal( , XsltSettings, XmlRes olver stylesheetResolver) bei System.Xml.Xsl.XslCompiledTransform.Load(String stylesheetUri, XsltSettings, XmlResolver stylesheetResolver) bei ( ).GetXslTransform(Boolean preTransform) bei ( ).get_XslHtmlOutput() bei (my ).get_DisplayMarkup()

+5
1

:

IDatabaseService XmlReader. , baseUri; :.

public XmlReader GetApplicationXslt(string applicationName)
{
    var baseUri = string.Format("db://{0}.hist.org", applicationName);
    return XmlReader.Create(input: …, 
                            settings: …,
                            baseUri: baseUri);  // <-- this one is important!
}

, . . , , .


, : :

, :

" XSLT . , XSLT- , ."

, ...

  • ( db://…)
  • XmlDbResolver db://
  • IDatabaseService, XmlDbResolver

, , , XmlResolver / IDatabaseService. , - .

mock XmlDbResolver ( ). , , IDatabaseService .

: . . OP .


:

Visual Studio 2010 ( , Gist Git (git clone https://gist.github.com/fbbd5e7319bd6c281c50b4ebb1cee1f9.git), 2- , git checkout d00629). .

Project items

( , Copy to output SqlServerDatabase.mdf, TestInput.xml .xslt Always.)


SqlServerDatabase.mdf:

, , SQL Server Express 2008. ( App.config, . .)

:

SqlServerDatabase structure

, :

ApplicationDocuments column definitions

. (. Program.cs CommonHistOrg.xslt).


App.config:

.

<?xml version="1.0"?>
<configuration>
  <connectionStrings>
    <add name="SqlServerDatabase"
         connectionString="Data Source=.\SQLEXPRESS;
                           AttachDbFilename=|DataDirectory|\SqlServerDatabase.mdf;
                           Integrated Security=True;
                           User Instance=True"
         />
  </connectionStrings>
</configuration>

IDatabaseService.cs:

IDatabaseService, .


SqlServerDatabaseService.cs:

, IDatabaseService. / :

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)
}

XmlDbResolver.cs:

XmlDbResolver, XmlDbResolver, :

  • IDatabaseService. DatabaseServiceFactory.DatabaseService.

  • Tracing.TraceHelper.WriteLine.


CommonHistOrg.xslt:

db://common.hist.org, (. Program.cs):

<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <xsl:template match="Foo">
    <Bar/>
  </xsl:template>
</xsl:stylesheet>

TestStylesheet.xml:

, db://common.hist.org:

<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <xsl:import href="db://common.hist.org"/>
</xsl:stylesheet>

TestInput.xml:

XML-, , TestStylesheet.xslt:

<?xml version="1.0" encoding="utf-8" ?>
<Foo/>

Program.cs:

:

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();
    }
}

: XML- <Bar/>, db://common.hist.org <Foo/> .


: :

  • Main:

    databaseService.StoreApplicationDocument(
        applicationName: "test",
        document:        XmlReader.Create("TestStylesheet.xslt"));
    
  • xslt.Load(@"TestStylesheet.xslt", …);
    

    do

    xslt.Load(@"db://test.hist.org", …);
    

    , OP.

, .

  • , Document XML. NTEXT.

  • , <?xml … ?> , . , XML , SqlServerDatabaseService .

, - .NET Framework. . .NET Framework. ( , 3.5 .) VS .

xslt.Load(…;) Main, LoadStylesheet XsltLoader.cs. a HybridDictionary documentUrisInUse, , -, URI . , URI, null ; .

, URI , IDatabaseService, . , baseUri XmlReader. . . this Gist (git clone https://gist.github.com/fbbd5e7319bd6c281c50b4ebb1cee1f9.git).

+8

All Articles