【问题标题】:.NET: How to insert XML document into SQL Server.NET:如何将 XML 文档插入 SQL Server
【发布时间】:2010-08-26 11:59:37
【问题描述】:

我想将任意 XML 插入 SQL Server。 XML 包含在 XmlDocument 对象中。

我要插入的列是nvarcharntextxml 列(如果它使您的生活更轻松,那么您可以选择它是哪种类型。真的是xml 列。)

原型

void SaveXmlToDatabase(DbConnection connection,
      XmlDocument xmlToSave,
      String tableName, String columnName);
{

}

我问的原因是因为我正在尝试找到将XmlDocument 转换为数据库可以采用的正确方法 - 确保编码正确:

  • 我必须确保插入期间使用的编码与数据库采用的编码相匹配
  • 我必须同步 <?xml version="1.0" encoding="windows-1252"?> 元素

我知道 ntextnvarcharxml 在 SQL Server 中存储为 UTF-16。所以我必须确保将数据以 UTF-16 格式提供给 SQL Server。这对于 .NET 中的 Strings 来说不是问题,因为它们 unicode UTF-16。

第二个问题,同步编码属性,是一个更难破解的难题。我必须弄清楚如何通过XmlDocument对象找到声明元素:

<?xml version="1.0" encoding="windows-1252"?>   (or whatever the encoding may be)

并将其调整为 UTF-16

<?xml version="1.0" encoding="UTF-16"?>

我天真的尝试(失败了)

忽略 XML 声明中的编码,只是弄清楚如何将任何内容保存到 SQL Server 中:

void SaveXmlToDatabase(DbConnection connection,
      XmlDocument xmlToSave,
      String tableName, String columnName);
{
   String sql = "INSERT INTO "+tableName+" ("+columnName+") 
          VALUES ('"+xmlToSave.ToString()+"')";

   using (DbCommand command = connection.CreateCommand())
   {
      command.CommandText = sql;

      DbTransaction trans = connection.BeginTransaction();
      try
      {
         command.ExecuteNonQuery();
         trans.Commit();
      }
      catch (Exception)
      {
         trans.Rollback();
         throw;
      }
   }
}

这失败了,因为我尝试运行的 sql 是:

INSERT INTO LiveData (RawXML) 
VALUES ('System.Xml.XmlDocument')

这是因为XmlDocument.ToString() 返回“System.Xml.XmlDocument”。查看实现,它确实在调用:

this.GetType().ToString();

旁白:微软似乎已经竭尽全力阻止你 从获取 Xml 作为字符串 - 大概是因为它会导致错误 (但他们没有告诉我们什么错误,为什么 它们是错误,或者是正确的方法 将 XmlDocument 转换为 String!)

另见

【问题讨论】:

标签: sql-server xml sql-server-2008 ado.net xml-serialization


【解决方案1】:

您必须使用 SqlParameter。 我建议这样做:

command.Parameters.Add(
   new SqlParameter("@xml", SqlDbType.Xml) 
       {Value = new SqlXml(new XmlTextReader(xmlToSave.InnerXml
                       , XmlNodeType.Document, null)) })

SQL 应该是这样的:

String sql = "INSERT INTO "+tableName+" ("+columnName+") VALUES (@xml)";

而且由于第一个子节点始终是 xml 节点,因此可以将编码替换为以下语句。

xmlToSave.FirstChild.InnerText = "version=\"1.0\" encoding=\"UTF-16\"";

总而言之,它看起来像这样:

void SaveXmlToDatabase(DbConnection connection,
      XmlDocument xmlToSave,
      String tableName, String columnName);
{
   String sql = "INSERT INTO "+tableName+" ("+columnName+") VALUES (@xml)";

   using (DbCommand command = connection.CreateCommand())
   {
      xmlToSave.FirstChild.InnerText = "version=\"1.0\" encoding=\"UTF-16\"";             
      command.CommandText = sql;
      command.Parameters.Add(
        new SqlParameter("@xml", SqlDbType.Xml) 
           {Value = new SqlXml(new XmlTextReader(xmlToSave.InnerXml
                       , XmlNodeType.Document, null)) });


      DbTransaction trans = connection.BeginTransaction();
      try
      {
         command.ExecuteNonQuery();
         trans.Commit();
      }
      catch (Exception)
      {
         trans.Rollback();
         throw;
      }
   }
}

【讨论】:

  • 我修改了SqlParameter Value,它必须是SqlXml类型的值
  • 我认为您的意思不是xmlToSave.ToString(),因为它返回字符串“System.Xml.XmlDocument”,它不是有效的XML。
  • 当然,你是对的。据我记得它必须b。 xmlToSave.Value
  • Ups - 用一个例子重新测试它;)它是 xmlToSave.InnerXml var doc = new XmlDocument(); doc.Load(@"d:\temp\test.htm"); Console.Out.WriteLine(doc.InnerXml);
  • 还添加了 xml 编码的替换 ;)
【解决方案2】:

最简单的解决方案是使用文档的OuterXml 属性。我在无法执行存储过程的情况下尝试解决问题时遇到了您的问题。 OuterXml 返回您期望从 .Value 或 .ToString() 获得的文本字符串

void SaveXmlToDatabase(DbConnection connection, 
      XmlDocument xmlToSave, 
      String tableName, String columnName); 
{ 
   String sql = "INSERT INTO "+tableName+" ("+columnName+")  
          VALUES ('"+xmlToSave.OuterXml+"')"; 

   using (DbCommand command = connection.CreateCommand()) 
   { 
      command.CommandText = sql; 

      DbTransaction trans = connection.BeginTransaction(); 
      try 
      { 
         command.ExecuteNonQuery(); 
         trans.Commit(); 
      } 
      catch (Exception) 
      { 
         trans.Rollback(); 
         throw; 
      } 
   } 
} 

【讨论】:

    【解决方案3】:

    迟到总比没有好...我认为您正在寻找这样的东西:

    void SaveXmlToDatabase(DbConnection connection,
          XmlDocument xmlToSave,
          String tableName, String columnName)
    {
       String sql = "INSERT INTO "+tableName+" ("+columnName+") 
              VALUES (@XmlVal)";
    
       using (DbCommand command = connection.CreateCommand())
       {
          command.CommandText = sql;
          command.Parameters.AddWithValue("XmlVal", new SqlXml(new XmlNodeReader(xmlToSave)));
    
          DbTransaction trans = connection.BeginTransaction();
          try
          {
             command.ExecuteNonQuery();
             trans.Commit();
          }
          catch (Exception)
          {
             trans.Rollback();
             throw;
          }
       }
    }
    

    XmlNodeReader 对象遍历并正确编码 XmlDocument(或任何其他 XmlNode),SqlXml 对象将其封装到适合与参数一起使用的 SqlDbType 中。这比使用字符串中介更安全,可能更有效。

    【讨论】:

      【解决方案4】:

      只是想添加一个等效于 Jonathan Overholt 解决方案的 SQLConnection:

      private void StoreXMLInDatabase(XmlDocument doc)
      {
          // xmlvalue is the name of the database column you want to insert into
          String sql = "INSERT INTO [tablename] (xmlvalue) VALUES (@xmlvalue)";
          // In order to read out you connection string from app.config, remember first to add a reference to System.Configuration in references,
          // and set using System.Configuration; in the top of the file
          string connString = ConfigurationManager.ConnectionStrings["DefaultConnection"].ConnectionString;  
          using (SqlConnection conn = new SqlConnection(connString))
          {
              if (conn.State == ConnectionState.Closed)
              {
                  conn.Open();
              }
              SqlTransaction transaction = conn.BeginTransaction();
              try
              {
                  // Remember to specify the SqlTransaction *name* (transaction)
                  SqlCommand cobj = new SqlCommand(sql, conn, transaction);
                  cobj.CommandText = sql;
                  cobj.Parameters.AddWithValue("xmlvalue", new SqlXml(new XmlNodeReader(doc)));
                  cobj.ExecuteNonQuery();
                  transaction.Commit();
              }
              catch (SqlException ex)
              {
                  //2627 = inserting duplicate key fail. Lets the procedure continue although an identity insert fail occurs
                  if (ex.Number == 2627)
                  {
                      transaction.Rollback();
                  }
              }
          } // end using
      }
      

      在 Visual Studio 2017 和 2013 中使用 .net 版本 4.6.2 和 MS SQL Server 2008 R2、2012 和 2016 测试正常

      如果身份插入失败并且您想停止该过程,则只需在 transaction.Rollback() 之后插入一行,如下所示:

      throw;
      

      或者您可以在任何情况下通过放置 transaction.Rollback(); 来停止该过程。并抛出;条件(if)之外的行

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2017-12-17
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2013-03-18
        • 2012-01-29
        • 1970-01-01
        相关资源
        最近更新 更多