Sqlserver
 sql >> Database >  >> RDS >> Sqlserver

Come risolvere l'impossibilità di cambiare l'errore di codifica durante l'inserimento di XML in SQL Server

Questa domanda è quasi un duplicato di altre 2 e, sorprendentemente, sebbene questa sia la più recente, credo che manchi la risposta migliore.

I duplicati, e quelle che credo siano le loro migliori risposte, sono:

  • Utilizzo di StringWriter per la serializzazione XML (14-10-2009)
    • https://stackoverflow.com/a/1566154/751158
  • Il tentativo di archiviare contenuto XML in SQL Server 2005 non riesce (problema di codifica) (21-12-2008)
    • https://stackoverflow.com/a/1091209/751158

Alla fine, non importa quale codifica venga dichiarata o utilizzata, purché XmlReader può analizzarlo localmente all'interno del server delle applicazioni.

Come è stato confermato in Il modo più efficiente per leggere XML in ADO.net dalla colonna di tipo XML in SQL Server?, SQL Server archivia XML in un formato binario efficiente. Usando SqlXml class, ADO.net può comunicare con SQL Server in questo formato binario e non richiede al server di database di eseguire alcuna serializzazione o deserializzazione di XML. Questo dovrebbe anche essere più efficiente per il trasporto attraverso la rete.

Usando SqlXml , XML verrà inviato pre-parsato al database, quindi il DB non ha bisogno di sapere nulla sulle codifiche dei caratteri - UTF-16 o altro. In particolare, si noti che le dichiarazioni XML non sono nemmeno persistenti con i dati nel database, indipendentemente dal metodo utilizzato per inserirli.

Si prega di fare riferimento alle risposte sopra collegate per metodi che sembrano molto simili a questo, ma questo esempio è il mio:

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();
            }
        }
    }
}

Nota che non considererei l'ultimo esempio (non commentato) "pronto per la produzione", ma lo lascerei così com'è per essere conciso e leggibile. Se fatto correttamente, sia il StringReader e il XmlReader creato dovrebbe essere inizializzato all'interno di using istruzioni per garantire che il loro Close() i metodi vengono chiamati una volta completati.

Da quello che ho visto, le dichiarazioni XML non vengono mai mantenute quando si utilizza una colonna XML. Anche senza usare .NET e usando semplicemente questa istruzione di inserimento SQL diretta, ad esempio, la dichiarazione XML non viene salvata nel database con l'XML:

Insert Into TestData(Xml) Values ('<?xml version="1.0" encoding="UTF-8"?><Test/>');

Ora in termini di domanda dell'OP, l'oggetto da serializzare deve ancora essere convertito in una struttura XML da MyMessage oggetto e XmlSerializer è ancora necessario per questo. Tuttavia, nel peggiore dei casi, invece di serializzare su una stringa, il messaggio potrebbe invece essere serializzato su un XmlDocument - che può quindi essere passato a SqlXml tramite un nuovo XmlNodeReader - evitare un viaggio di deserializzazione/serializzazione su una stringa. (Vedi http://blogs.msdn.com/b/jongallant/archive/2007/01/30/how-to-convert-xmldocument-to-xmlreader-for-sqlxml-data-type.aspx per dettagli e un esempio .)

Tutto qui è stato sviluppato e testato con .NET 4.0 e SQL Server 2008 R2.

Per favore, non sprecare eseguendo XML tramite conversioni extra (deserializzazione e serializzazione - in DOM, stringhe o altro), come mostrato in altre risposte qui e altrove.