【问题标题】:Write SqlDataReader to XML file将 SqlDataReader 写入 XML 文件
【发布时间】:2015-07-27 21:02:13
【问题描述】:

我正在用 C# 测试存储的 SQL 过程。 procs 返回数据类型 SqlDataReader,我想将整个内容写入 XML 文件以便稍后进行比较。我读过的任何内容都没有提供非常简单的解决方案。有没有办法在不遍历流中的所有数据的情况下做到这一点?我对 SQL 了解不多,所以我不确定我在这里使用的是什么。

【问题讨论】:

  • 数据集或数据表可以轻松写入 XML。 DataSet 和 DataTable 一次将所有结果拉入内存 - 因此,如果您的结果不是太大而无法放入内存,您可以切换到那些而不是 DataReader。
  • 有人可能会注意到DataSet 使用的 XML Schema 从人类可读性的角度来看还有一些不足之处。如果您的 QA 人员试图测试和区分事物,这很重要。

标签: c# sql xml sqldatareader


【解决方案1】:

DataSetDataTable 及其同类生成的 XML 从人类阅读它的角度来看还有一些不足之处。我会自己动手。

一个SqlDataReader(不管它是从存储过程返回数据还是从纯文本SQL 查询返回数据),对许多结果集返回0。每个这样的结果集都有

  • 描述每行返回的列的模式,以及
  • 结果集本身,由零个或多个组成。
  • 每一行本质上是一个由 1 个或多个组成的数组,每个单元格都包含行中具有该序号位置的列的值。
  • 每个此类列都有某些属性,其中一些来自架构,例如名称、序数类型、可空性等。
  • 最后,一行中的列值是一个object,其类型与结果中列的SQL Server数据类型相对应……如果该列为空,则为DbNull.Value

基本循环非常简单(MSDN 中有很多关于如何实现的示例。)虽然一开始编写它可能需要一些工作,但一旦编写完成,它就可以全面使用,所以它是一次性的。我建议做这样的事情:

  1. 确定您希望 XML 的外观。假设您的意图是能够不时区分结果,我可能会选择这样的东西(因为我喜欢保持简洁并避免冗余):

    <stored-procedure-results>
      <name> dbo.some-stored-procedure-name </name>
      <result-sets>
        <result-set>
          <column-schema column-count="N">
            <column ordinal="0...N-1" name="column-name-or-null-if-column-is-unnamed-or-not-unique" data-type=".net-data-type" nullable="true|false" />
            ...
          </schema>
          <rows>
            <row>
              <column ordinal="0..N-1" value="..." />
              ...
            <row/>
            ...
          </rows>
        </result-set>
        ...
      </result-sets>
    </stored-procedure-results>
    
  2. 构建 POCO 模型类以包含数据。使用 XML 序列化属性对它们进行属性化以获得所需的标记。从上面的 XML 示例中,这些类不会那么复杂。您可能希望将列值表示为字符串而不是本机数据类型。

  3. 构建将运行数据读取器并构建模型的映射器。

然后是几十行代码来构建选择的 XML 序列化程序并输出格式良好的 XML。

注意事项:

  • 出于 QA 目的,您可能想要捕获传递给查询的参数(如果有)以及查询本身,可能还有运行的日期/时间。

    李>
  • 在一些奇怪的情况下,我描述的结果集模型可能会变得……不可靠。例如,使用compute by 的选择语句必须以不同方式处理。根据我的经验,忽略这种边缘情况是非常安全的,因为您不太可能在野外遇到这样的查询。

  • 考虑一下您如何在 XML 中表示 nullnull 字符串 字符串相同。

    李>

【讨论】:

    【解决方案2】:

    试试这个

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Data;
    using System.Data.SqlClient;
    
    namespace ConsoleApplication1
    {
        class Program
        {
            const string FILENAME = @"C:\temp\test.xml";
            static void Main(string[] args)
            {
                string connstr = "Enter your connection string here";
                string SQL = "Enter your SQL Here";
    
                SqlDataAdapter adapter = new SqlDataAdapter(SQL, connstr);
                SqlCommand cmd = adapter.SelectCommand;
                cmd.Parameters.Add("abc", SqlDbType.VarChar);
    
    
                adapter.SelectCommand.ExecuteNonQuery();
    
                DataSet ds = new DataSet();
                adapter.Fill(ds);
    
                ds.WriteXml(FILENAME, XmlWriteMode.WriteSchema);
            }
        }
    }
    

    【讨论】:

    • 如何添加参数以运行它?或者,我整理了一个将 SqlDataReader 转换为 DataSet 的方法,但由于某种原因,它只会将数据类型检索为 RunTimeType
    • 好的,谢谢。 DataAdapter 和 DataReader 非常相似。无论如何,我让我的 DataSet 转换器工作了,但如果它最终运行时间太长,我可能会回到这个。
    • DataReader 你必须枚举行。 DataAdapter 会自动为您进行枚举。
    • 如果DB很大,就会出现outofmemory异常。
    • 你指的是多大?我用过 > 10MB。
    【解决方案3】:

    我看到主要问题是如何在发布之前测试复杂的存储过程,而不是从 SQLDataAdapter 编写一个非常简单的 XML。逐行,逐列。 您有一个不包含静态数据的测试数据库,并且您以某种方式存储了不同版本的存储过程。 一个简单的设置是运行您拥有的存储过程的(比如说 5 个)版本,针对相同的版本运行它们 数据库内容,将 xml 存储到文件夹并进行比较。例如,我会为每次运行使用不同的文件夹,并有一个时间戳来区分它们。我不会在 xml 的编写方式上花费太多,并且为了检测它们是否不同,即使使用 String.Compare(fileStream1.ReadToEnd(), fileStream2.ReadToEnd()) 也会结束。如果结果太大,则需要更详细的内容。 如果 2 个 xml 之间存在差异,那么您可以使用文本比较工具查看它们。 ...对于具有多个连接的更复杂的存储过程,最常见的区别可能是 xmls 的大小\返回的行数,而不是字段的值。

    在生产中,数据库的内容不是静态的,所以做这种类型的测试没有意义。

    【讨论】:

    • 就是这样,我就是无法创建文件。
    【解决方案4】:

    当使用accepted answer中描述的DataTable或DataSet中的内置方法WriteXml序列化SqlDataReader,并且数据包含地理数据时,地理数据丢失并且无法恢复后者。

    更多详情请阅读Datatable with SqlGeography column can't be serialized to xml correctly with loss of Lat,Long and other elements

    @dbc 提供了一种解决方法,可以在不丢失数据的情况下保存到 xml,并使用相同的内置方法 WriteXml 保存到 xml。 Try it online

    【讨论】:

      猜你喜欢
      • 2016-08-02
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-12-21
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多