.NET: Как вставить XML-документ в SQL Server - PullRequest
10 голосов
/ 26 августа 2010

Я хочу вставить произвольный XML в SQL Server.XML содержится в объекте XmlDocument.

Столбец, в который я хочу вставить, - это либо столбец nvarchar, ntext, либо xml (если это облегчит вашу жизнь, вы можете выбратькакой это тип. На самом деле это xml столбец.)

Прототип

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

}

Причина, по которой я спрашиваю, заключается в том, что я пытаюсь найти правильный способ поворота XmlDocument во что-то, что может принять база данных - обязательно сохраняя правильное кодирование:

  • Я должен убедиться, что кодировка, используемая во время вставки, совпадает с кодировкой базы данных
  • Мне нужно синхронизировать<?xml version="1.0" encoding="windows-1252"?> элемент

Я знаю, ntext, nvarchar или xml хранятся как UTF-16 внутри SQL Server.Поэтому я должен быть уверен, что данные SQL Server будут передаваться как UTF-16.Это не проблема для String s в .NET, так как они являются 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();

В сторону: Microsoft, похоже, старается изо всех сил помешать вам получить XML какстрока - предположительно потому, что она приводит к ошибкам (но они не сообщают нам, какие ошибки, почему они являются ошибками, или правильный способ преобразования XmlDocument в String!)

См. Также

Ответы [ 4 ]

9 голосов
/ 26 августа 2010

Вы должны использовать 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;
      }
   }
}
2 голосов
/ 10 июля 2012

Самое простое решение - использовать атрибут 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; 
      } 
   } 
} 
1 голос
/ 17 апреля 2018

Лучше поздно, чем никогда ... Я думаю, вы ищете что-то вроде этого:

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, подходящий для использования с параметром. Это безопаснее и, вероятно, более эффективно, чем использование строкового посредника.

0 голосов
/ 30 августа 2018

Просто хотел добавить 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

Если вставка идентификатора завершилась неудачно и вы хотите остановить процедуру, просто вставьте строку после транзакции.Rollback (), например:

throw;

Или вы можете остановить процедуру при любых обстоятельствах, установив транзакцию. Rollback (); и бросить; строки за пределами условного (если)

...