Sqlserver
 sql >> Base de Dados >  >> RDS >> Sqlserver

Como resolver incapaz de alternar o erro de codificação ao inserir XML no SQL Server


Esta pergunta é quase uma duplicata de outras 2 e, surpreendentemente - embora esta seja a mais recente - acredito que está faltando a melhor resposta.

As duplicatas, e o que acredito serem suas melhores respostas, são:
  • Usando StringWriter para serialização XML (2009-10-14)
    • https://stackoverflow.com/a/1566154/751158
  • A tentativa de armazenar conteúdo XML no SQL Server 2005 falha (problema de codificação) (2008-12-21)
    • https://stackoverflow.com/a/1091209/751158

No final, não importa qual codificação é declarada ou usada, desde que o XmlReader pode analisá-lo localmente no servidor de aplicativos.

Como foi confirmado em Maneira mais eficiente de ler XML no ADO.net da coluna tipo XML no SQL Server?, o SQL Server armazena XML em um formato binário eficiente. Usando o SqlXml class, o ADO.net pode se comunicar com o SQL Server neste formato binário e não exigir que o servidor de banco de dados faça qualquer serialização ou desserialização de XML. Isso também deve ser mais eficiente para o transporte através da rede.

Usando SqlXml , o XML será enviado pré-analisado para o banco de dados e, em seguida, o banco de dados não precisará saber nada sobre codificações de caracteres - UTF-16 ou não. Em particular, observe que as declarações XML nem são persistidas com os dados no banco de dados, independentemente de qual método é usado para inseri-lo.

Consulte as respostas vinculadas acima para métodos que se parecem muito com isso, mas este exemplo é meu:
using System.Data;
using System.Data.SqlClient;
using System.Data.SqlTypes;
using System.IO;
using System.Xml;

static class XmlDemo {
    static void Main(string[] args) {
        using(SqlConnection conn = new SqlConnection()) {
            conn.ConnectionString = "...";
            conn.Open();

            using(SqlCommand cmd = new SqlCommand("Insert Into TestData(Xml) Values (@Xml)", conn)) {

                cmd.Parameters.Add(new SqlParameter("@Xml", SqlDbType.Xml) {
                    // Works.
                    // Value = "<Test/>"

                    // Works.  XML Declaration is not persisted!
                    // Value = "<?xml version=\"1.0\"?><Test/>"

                    // Works.  XML Declaration is not persisted!
                    // Value = "<?xml version=\"1.0\" encoding=\"UTF-16\"?><Test/>"

                    // Error ("unable to switch the encoding" SqlException).
                    // Value = "<?xml version=\"1.0\" encoding=\"UTF-8\"?><Test/>"

                    // Works.  XML Declaration is not persisted!
                    Value = new SqlXml(XmlReader.Create(new StringReader("<?xml version=\"1.0\" encoding=\"UTF-8\"?><Test/>")))
                });

                cmd.ExecuteNonQuery();
            }
        }
    }
}

Observe que eu não consideraria o último exemplo (não comentado) como "pronto para produção", mas o deixei como está para ser conciso e legível. Se feito corretamente, tanto o StringReader e o XmlReader criado deve ser inicializado dentro de using instruções para garantir que seu Close() métodos são chamados quando concluídos.

Pelo que vi, as declarações XML nunca são persistidas ao usar uma coluna XML. Mesmo sem usar .NET e usando apenas essa instrução SQL direta, por exemplo, a declaração XML não é salva no banco de dados com o XML:
Insert Into TestData(Xml) Values ('<?xml version="1.0" encoding="UTF-8"?><Test/>');

Agora em termos da pergunta do OP, o objeto a ser serializado ainda precisa ser convertido em uma estrutura XML do MyMessage objeto e XmlSerializer ainda é necessário para isso. No entanto, na pior das hipóteses, em vez de serializar para uma String, a mensagem pode ser serializada para um XmlDocument - que pode ser passado para SqlXml através de um novo XmlNodeReader - evitando uma viagem de desserialização/serialização para uma string. (Consulte http://blogs.msdn.com/b/jongallant/archive/2007/01/30/how-to-convert-xmldocument-to-xmlreader-for-sqlxml-data-type.aspx para obter detalhes e um exemplo .)

Tudo aqui foi desenvolvido e testado com .NET 4.0 e SQL Server 2008 R2.

Por favor, não desperdice executando XML por meio de conversões extras (desserializações e serializações - para DOM, strings ou outros), conforme mostrado em outras respostas aqui e em outros lugares.