Sql server .NET:如何将XML文档插入SQL Server

Sql server .NET:如何将XML文档插入SQL Server,sql-server,xml,sql-server-2008,ado.net,xml-serialization,Sql Server,Xml,Sql Server 2008,Ado.net,Xml Serialization,我想将任意XML插入SQL Server。XML包含在XmlDocument对象中 我想插入的列可以是nvarchar,ntext,也可以是xml列(如果它能让您的生活更轻松,那么您可以选择它是哪种类型。实际上,它是xml列。) 原型 我问这个问题的原因是因为我试图找到正确的方法将XmlDocument转换为数据库可以使用的内容-确保编码正确: 我必须确保插入期间使用的编码与数据库所采用的编码匹配 我必须同步元素 我知道ntext、nvarchar或xml在SQL Server中存储为UTF

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

我想插入的列可以是
nvarchar
ntext
,也可以是
xml
列(如果它能让您的生活更轻松,那么您可以选择它是哪种类型。实际上,它是
xml
列。)

原型 我问这个问题的原因是因为我试图找到正确的方法将
XmlDocument
转换为数据库可以使用的内容-确保编码正确:

  • 我必须确保插入期间使用的编码与数据库所采用的编码匹配
  • 我必须同步
    元素
我知道
ntext
nvarchar
xml
在SQL Server中存储为
UTF-16
。因此,我必须确保将数据作为UTF-16提供给SQL Server。对于.NET中的
String
s来说,这不是问题,因为它们unicode UTF-16

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

<?xml version="1.0" encoding="windows-1252"?>   (or whatever the encoding may be)
这会失败,因为我尝试运行的
sql
是:

INSERT INTO LiveData (RawXML) 
VALUES ('System.Xml.XmlDocument')
这是因为
XmlDocument.ToString()
返回“
System.Xml.XmlDocument
”。窥视一下实现,它会发现它实际上在调用:

this.GetType().ToString();
旁白:微软似乎特意阻止了你 将Xml作为字符串获取- 大概是因为它会导致bug (但他们没有告诉我们什么是bug,为什么 它们是虫子,或者说是正确的方法 将
XmlDocument
转换为
字符串
!)

另见

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

command.Parameters.Add(
   new SqlParameter("@xml", SqlDbType.Xml) 
       {Value = new SqlXml(new XmlTextReader(xmlToSave.InnerXml
                       , XmlNodeType.Document, null)) })
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;
      }
   }
}
SQL应该如下所示:

String sql = "INSERT INTO "+tableName+" ("+columnName+") VALUES (@xml)";
由于第一个子节点始终是xml节点,因此可以使用以下语句替换编码

xmlToSave.FirstChild.InnerText = "version=\"1.0\" encoding=\"UTF-16\"";
总而言之,它看起来是这样的:

command.Parameters.Add(
   new SqlParameter("@xml", SqlDbType.Xml) 
       {Value = new SqlXml(new XmlTextReader(xmlToSave.InnerXml
                       , XmlNodeType.Document, null)) })
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;
      }
   }
}

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


迟做总比不做好。。。我想你在找这样的东西:

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;
      }
   }
}
throw;

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

只是想添加一个与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()之后插入一行,如下所示:

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;
      }
   }
}
throw;

或者,您可以在任何情况下通过放置事务来停止该过程。Rollback();和扔;在条件(if)之外的行

这个问题可能是相关的:我修改了SqlParameter值,它必须是sqlxml类型的值。我想你不是指
xmlToSave.ToString()
,因为它返回字符串“
System.Xml.XmlDocument
”,它不是有效的Xml。当然,你是对的。据我记忆所及,一定是b。xmlToSave.ValueUps-使用示例重新设置;)它是xmlToSave.InnerXml var doc=new XmlDocument();文件加载(@“d:\temp\test.htm”);Console.Out.WriteLine(doc.InnerXml);还添加了xml编码的替换;)