2009-04-14 16 views
6

Tengo un requisito para pasar los parámetros como Xml a mis procedimientos almacenados.Pasar parámetros como Xml a un procedimiento almacenado

Tengo un servicio WCF en el nivel medio que realiza llamadas a mi capa de datos, que a su vez reenvía la solicitud al procedimiento almacenado apropiado.

El diseño es que el servicio WCF es responsable de construir el Xml para pasarlo al Repositorio.

Me pregunto si debo mantener el control de los parámetros contenidos en el Xml en el nivel intermedio o usar un diccionario creado por el cliente que luego convierto a Xml en el nivel intermedio.

En el momento que he pasado por esta última - por ejemplo:

public TestQueryResponseMessage TestQuery(TestQueryRequestMessage message) 
{ 
     var result = Repository.ExecuteQuery("TestQuery", ParamsToXml(message.Body.Params)); 

     return new TestQueryResponseMessage 
     { 
      Body = new TestQueryResponse 
      { 
       TopicItems = result; 
      } 
     } 
    } 


private string ParamsToXml(Dictionary<string, string> nvc) 
{ 
     //TODO: Refactor 
     StringBuilder sb = new StringBuilder(); 

     sb.Append("<params>"); 
     foreach (KeyValuePair<string, string> param in nvc) 
     { 
      sb.Append("<param>"); 
      sb.Append("<" + param.Key + ">"); 
      sb.Append(param.Value); 
      sb.Append("</" + param.Key + ">"); 
      sb.Append("</param>"); 
     } 
     sb.Append("</params>"); 

     return sb.ToString(); 
} 

Sin embargo puede ser que necesite hacerlo de la primera forma. P.ej.

public TestQueryResponseMessage TestQuery(TestQueryRequestMessage message) 
{ 
     string xml = string.Format("<params><TestParameter>{0}</TestParameter></params>",message.Body.TestParameter) 

     var result = Repository.ExecuteQuery("TestQuery", xml); 

     return new TestQueryResponseMessage 
     { 
      Body = new TestQueryResponse 
      { 
        TopicItems = result; 
      } 
     } 
} 

¿Qué recomienda hivemind?

+1

Cosas como string.Format no son adecuadas para crear xml, ya que existen reglas de escape complejas que deben seguirse. XmlWriter sería una alternativa razonable para el caso del diccionario, pero considere el objeto orientado abordado a continuación. –

Respuesta

6

Si debe usar xml; a continuación, en lugar de pasar alrededor de un diccionario, que haría uso de una clase que representa que los datos, y utilizan XmlSerializer a buscarla como xml:

[Serializable, XmlRoot("args")] 
public class SomeArgs { 
    [XmlElement("foo")] public string Foo { get; set; } 
    [XmlAttribute("bar")] public int Bar { get; set; } 
} 
... 
SomeArgs args = new SomeArgs { Foo = "abc", Bar = 123 }; 
XmlSerializer ser = new XmlSerializer(typeof(SomeArgs)); 
StringWriter sw = new StringWriter(); 
ser.Serialize(sw, args); 
string xml = sw.ToString(); 

Esto hace que sea mucho más fácil de manejar, que se aplica a los argumentos que consulta, en una forma orientada a objetos. También significa que no tiene que hacer su propio escape xml ...

+0

Creo que tiene razón, gracias. Intenté ponerlo en funcionamiento rápidamente con una explosión de clase mínima, pero puedo ver que se convertirá en un desastre inmanejable de esa manera. –

+0

@Marc Gravell: ¿Recomendaría el mismo o diferente enfoque con VS2010/.Net 4.0? – FMFF

+0

@FMFF Intento evitar XML en la base de datos para ser honesto –

0

Pondría el código de construcción xml dentro del objeto de dominio. De esta forma, puede llamar a obj.GetXML() desde el servicio web o la capa de datos.

+0

No encuentro que "Obj" deba llamar directamente a "GetXml()". No pertenece allí. El tipo de "obj" debe hacer lo que se supone que es, a menos que sea el trabajo de formatear y generar XML. – Sung

+0

puede hacer lo mismo con un método de extensión, de esa manera no modificará el objeto de dominio –

1

es posible que utilices una clase de objeto serialización como esto

public class Serialization 
    { 
     /// <summary> 
     /// Serializes the object. 
     /// </summary> 
     /// <param name="myObject">My object.</param> 
     /// <returns></returns> 
     public static XmlDocument SerializeObject(Object myObject) 
     { 
      XmlDocument XmlObject = new XmlDocument(); 
      String XmlizedString = string.Empty; 

      try 
      {     
       MemoryStream memoryStream = new MemoryStream(); 
       XmlSerializer xs = new XmlSerializer(myObject.GetType()); 
       XmlTextWriter xmlTextWriter = new XmlTextWriter(memoryStream, Encoding.UTF8); 
       xs.Serialize(xmlTextWriter, myObject); 
       memoryStream = (MemoryStream)xmlTextWriter.BaseStream; 
       XmlizedString = UTF8ByteArrayToString(memoryStream.ToArray());     
      } 
      catch (Exception e) 
      { 
       System.Console.WriteLine(e); 
      } 
      XmlObject.LoadXml(XmlizedString); 
      return XmlObject;    
     } 

     /// <summary> 
     /// Deserializes the object. 
     /// </summary> 
     /// <typeparam name="T"></typeparam> 
     /// <param name="XmlizedString">The p xmlized string.</param> 
     /// <returns></returns> 
     public static T DeserializeObject<T>(String XmlizedString) 
     { 
      XmlSerializer xs = new XmlSerializer(typeof(T)); 
      MemoryStream memoryStream = new MemoryStream(StringToUTF8ByteArray(XmlizedString)); 
      //XmlTextWriter xmlTextWriter = new XmlTextWriter(memoryStream, Encoding.UTF8); 
      Object myObject = xs.Deserialize(memoryStream); 
      return (T)myObject; 
     } 

     /// <summary> 
     /// To convert a Byte Array of Unicode values (UTF-8 encoded) to a complete String. 
     /// </summary> 
     /// <param name="characters">Unicode Byte Array to be converted to String</param> 
     /// <returns>String converted from Unicode Byte Array</returns> 
     private static String UTF8ByteArrayToString(Byte[] characters) 
     { 
      UTF8Encoding encoding = new UTF8Encoding(); 
      String constructedString = encoding.GetString(characters); 
      return (constructedString); 
     } 



     /// <summary> 
     /// Converts the String to UTF8 Byte array and is used in De serialization 
     /// </summary> 
     /// <param name="pXmlString"></param> 
     /// <returns></returns> 
     private static Byte[] StringToUTF8ByteArray(String pXmlString) 
     { 
      UTF8Encoding encoding = new UTF8Encoding(); 
      Byte[] byteArray = encoding.GetBytes(pXmlString); 
      return byteArray; 
     } 
    } 

entonces usted no tiene que construir el XML a mano, además de que puede usar esto con cualquier elemento para transformarla mediante XSLT

+2

Eso (MemoryStream, Codificación, etc.) es la forma difícil de construir el xml; solo use un StringWriter ... mucho más fácil y más eficiente (sin codificación, sin bytes [], etc.) –

3

Una vez que utiliza la solución Bob The Janitor y tiene su XML.

Cree su procedimiento almacenado con un parámetro XML. Luego, dependiendo de la cantidad de XML que tenga y qué hace con él, puede usar Xquery o OpenXML para triturar el documento XML. Extraiga los datos y realice la acción correcta. Este ejemplo es básico y de tipo pseudocódigo, pero debes tener la idea.

CREATE PROCEDURE [usp_Customer_INS_By_XML] 
@Customer_XML XML 
AS 
BEGIN 
EXEC sp_xml_preparedocument @xmldoc OUTPUT, @Customer_XML 

--OPEN XML example of inserting multiple customers into a Table. 
INSERT INTO CUSTOMER 
(
First_Name 
Middle_Name 
Last_Name 
) 
SELECT 
First_Name 
,Middle_Name 
,Last_Name 
FROM OPENXML (@xmldoc, '/ArrayOfCustomers[1]/Customer',2) 
WITH(
First_Name VARCHAR(50) 
,Middle_Name VARCHR(50) 
,Last_Name VARCHAR(50) 
) 

EXEC sp_xml_removedocument @xmldoc 
END 
Cuestiones relacionadas