简短回答:
Your 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://…
paths)
- 实施并连接自定义
XmlDbResolver
用于处理db://
进口方案
- 以以下形式实现数据库访问代码
IDatabaseService
,它支持XmlDbResolver
如果除导入路径外样式表未更改,则错误可能出在您的XmlResolver
班级和/或在IDatabaseService
执行。由于您尚未显示后者的代码,因此我们无法在不进行猜测的情况下调试您的代码。
我使用你创建了一个模拟项目XmlDbResolver
(完整描述如下)。 我无法重现该错误,因此我怀疑您的IDatabaseService
执行导致错误。
Update:我已经能够重现该错误。请参阅OP的评论和本答案的最后一部分。
我尝试重现您的错误:
我在 Visual Studio 2010 中创建了一个控制台应用程序项目(您可以通过克隆来检索该项目)这个要点 https://gist.github.com/stakx/fbbd5e7319bd6c281c50b4ebb1cee1f9使用 Git (git clone https://gist.github.com/fbbd5e7319bd6c281c50b4ebb1cee1f9.git
)然后检查第二次提交,git checkout d00629
)。我将在下面更详细地描述解决方案的每个项目。
(请注意,复制到输出目录的财产SqlServerDatabase.mdf
, TestInput.xml
,以及两者的.xslt
项目项应设置为Always.)
SqlServerDatabase.mdf:
这是一个基于服务的数据库,我将把它附加到 SQL Server Express 2008 的本地实例。(这是通过中的连接字符串完成的)App.config
;见下文。)
我已经在此数据库中设置了以下项目:
该表包含两列,定义如下:
表格最初是空的。测试数据将在运行时添加到数据库中(参见Program.cs
and CommonHistOrg.xslt
below).
应用程序配置:
该文件包含上述数据库的连接字符串条目。
<?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
below):
<?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>
测试样式表.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>
测试输入.xml:
这是我们将使用上面的内容转换的 XML 测试输入文档TestStylesheet.xslt
:
<?xml version="1.0" encoding="utf-8" ?>
<Foo/>
程序.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
method:
databaseService.StoreApplicationDocument(
applicationName: "test",
document: XmlReader.Create("TestStylesheet.xslt"));
-
代替
xslt.Load(@"TestStylesheet.xslt", …);
do
xslt.Load(@"db://test.hist.org", …);
这会触发 OP 报告的错误。
经过一番调试,我发现以下情况不会导致这个问题。
事实是Document
数据库表中的列具有类型XML
。它失败了NTEXT
, too.
事实是<?xml … ?>
从数据库返回的文档中缺少标头。即使之前手动添加了 XML 标头,该错误仍然存在SqlServerDatabaseService
将控制权返回给框架。
事实上,该错误是在 .NET Framework 代码中的某个位置触发的。这就是为什么我决定下载并安装.NET框架参考源 http://referencesource.microsoft.com/。 (出于调试目的,我更改了解决方案以使用框架 3.5 版本。)安装此框架并重新启动 VS,然后您就可以在调试会话期间查看并单步执行框架代码。
从调用开始xslt.Load(…;)
in our Main
方法,我进入了框架代码,最终得出了一个方法LoadStylesheet
inside XsltLoader.cs
。有一个HybridDictionary
called documentUrisInUse
,它显然存储了已加载样式表的基本 URI。因此,如果我们加载多个带有空或缺失基本 URI 的样式表,此方法将尝试添加null
查那本字典两次;这就是导致错误的原因。
因此,一旦您为您返回的每个样式表分配了唯一的基本 URIIDatabaseService
,一切都应该工作正常。你可以通过传递一个来做到这一点baseUri
to the XmlReader
构造函数。请参阅我的答案开头的代码示例。您还可以通过以下方式检索更新的工作解决方案下载 https://gist.github.com/stakx/fbbd5e7319bd6c281c50b4ebb1cee1f9/archive/79efc4c57f4aeb490733c122da14b5a69fe86e8d.zip或克隆这个要点 https://gist.github.com/stakx/fbbd5e7319bd6c281c50b4ebb1cee1f9 (git clone https://gist.github.com/fbbd5e7319bd6c281c50b4ebb1cee1f9.git
).