sql >> Base de Datos >  >> RDS >> Sqlserver

Cómo resolver la imposibilidad de cambiar el error de codificación al insertar XML en SQL Server

Esta pregunta es casi un duplicado de otras 2 y, sorprendentemente, aunque esta es la más reciente, creo que le falta la mejor respuesta.

Los duplicados, y lo que creo que son sus mejores respuestas, son:

  • Uso de StringWriter para serialización XML (2009-10-14)
    • https://stackoverflow.com/a/1566154/751158
  • Intentar almacenar contenido XML en SQL Server 2005 falla (problema de codificación) (2008-12-21)
    • https://stackoverflow.com/a/1091209/751158

Al final, no importa qué codificación se declare o use, siempre que XmlReader puede analizarlo localmente dentro del servidor de aplicaciones.

Como se confirmó en ¿La forma más eficiente de leer XML en ADO.net desde la columna de tipo XML en el servidor SQL?, SQL Server almacena XML en un formato binario eficiente. Usando el SqlXml class, ADO.net puede comunicarse con SQL Server en este formato binario y no requiere que el servidor de la base de datos realice ninguna serialización o deserialización de XML. Esto también debería ser más eficiente para el transporte a través de la red.

Usando SqlXml , XML se enviará previamente analizado a la base de datos, y luego la base de datos no necesita saber nada sobre las codificaciones de caracteres, UTF-16 o de otro tipo. En particular, tenga en cuenta que las declaraciones XML ni siquiera persisten con los datos en la base de datos, independientemente del método que se utilice para insertarlos.

Consulte las respuestas vinculadas anteriormente para conocer los métodos que se parecen mucho a este, pero este ejemplo es mío:

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

Tenga en cuenta que no consideraría que el último ejemplo (sin comentarios) esté "listo para la producción", pero lo dejé como está para que sea conciso y legible. Si se hace correctamente, tanto el StringReader y el XmlReader creado debe inicializarse dentro de using declaraciones para asegurarse de que sus Close() los métodos se llaman cuando están completos.

Por lo que he visto, las declaraciones XML nunca se conservan cuando se usa una columna XML. Incluso sin usar .NET y solo usando esta declaración de inserción SQL directa, por ejemplo, la declaración XML no se guarda en la base de datos con el XML:

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

Ahora, en términos de la pregunta del OP, el objeto que se serializará aún debe convertirse en una estructura XML desde MyMessage objeto y XmlSerializer todavía se necesita para esto. Sin embargo, en el peor de los casos, en lugar de serializar en una cadena, el mensaje podría serializarse en un XmlDocument - que luego se puede pasar a SqlXml a través de un nuevo XmlNodeReader - evitar un viaje de deserialización/serialización a una cadena. (Consulte http://blogs.msdn.com/b/jongallant/archive/2007/01/30/how-to-convert-xmldocument-to-xmlreader-for-sqlxml-data-type.aspx para obtener detalles y un ejemplo .)

Todo aquí fue desarrollado y probado con .NET 4.0 y SQL Server 2008 R2.

Por favor, no hagas desperdicio ejecutando XML a través de conversiones adicionales (deserializaciones y serializaciones, a DOM, cadenas o de otro modo), como se muestra en otras respuestas aquí y en otros lugares.