Archivo de Octubre 2008


Salió el XNA Game Studio 3.0!!!

Viernes, 31 Oct, 2008 @ 12:38 | Por Gustavo Cantero (The Wolf) | Visual Studio, XNA

Ya salió el release final del XNA Game Studio 3.  Para quienes no lo conocen, el XNA Game Studio es una herramienta gratis de Microsoft para hobistas, estudiantes y desarrolladores independientes que facilita el desarrollo de videojuegos para Windows, XBOX 360 y Microsoft Zune, usando librerias basadas en .NET Framework.

Algunas de las nuevas características que se incluyen en esta versión son las siguientes:

  • Integración con la mayoria de los productos de la familia Visual Studio 2008
  • Soporte para C# 3.0 y LINQ
  • Soporte para desarrollar juegos para Microsoft Zune (en la versión anterior sólo se podia para Windows y XBOX)
  • Mejoras en las API del XNA Framework para el soporte multimedia
  • Efectos de sonido
  • Modo de “trial” en los juegos, para que los usuarios puedan probarlo antes de decidir su compra
  • y muchas novedades más……

Por último les dejo algunos enlaces de utilidad, incluyendo el de la descarga del XNA Game Studio 3:

VN:F [1.7.3_972]
Rating: 0.0/10 (0 votos cast)

Silverlight Toolkit

Miércoles, 29 Oct, 2008 @ 11:55 | Por Gustavo Cantero (The Wolf) | Silverlight


Para quienes ya estén trabajando con Silverlight 2.0, o para quienes recién estén incursionando en esta potente tecnología, ha salido en CodePlex el primer release del Silverlight Toolkit, un paquete de controles que permitirán desarrollar nuestras aplicaciones más fácil y rápido. En esta versión se encuentran en su versión estable los siguientes controles:

Y en su versión preview se encuentran los siguientes:

Además de estos controles, en este Toolkit se incluyen varios temas de estilo profesional para utilizar en nuestras aplicaciones.  A continuación detallo una lista con los mismos:

Sin duda, son buenas noticias para quienes estamos utilizando Silverlight.

VN:F [1.7.3_972]
Rating: 9.5/10 (2 votos cast)

Visual Studio 2010 y .NET Framework 4 CTP

Lunes, 27 Oct, 2008 @ 17:30 | Por Gustavo Cantero (The Wolf) | .NET Framework, Visual Studio

Ya está disponible el Community Technology Preview del Visual Studio 2010 y del .NET Framework 4!  Esta información está disponible en Microsoft Connect.  La dirección de donde bajarse el CTP es esta: http://www.microsoft.com/downloads/details.aspx?FamilyId=922B4655-93D0-4476-BDA4-94CF5F8D4814&displaylang=en.  Cabe mencionar que la descarga ocupa alrededor de 7.2 Gb. y es una máquina virtual para Microsoft Virtual PC 2007, lo que permite que hagamos pruebas con estos productos sin alterar o dañar ninguna de las otras versiones que podamos tener instaladas en nuestro PC.

VN:F [1.7.3_972]
Rating: 9.0/10 (3 votos cast)

Paginación en PHP orientada a objetos

Lunes, 20 Oct, 2008 @ 13:15 | Por Dario Krapp | MySQL 5, PHP

Hace unos días en un proyecto que estamos desarrollando con tecnología PHP 5.0 y MySql 5.0, viendo la documentación funcional descubrimos que teníamos una gran cantidad de formularios los cuales realizarían tareas de listado, altas, bajas y modificación de datos.
Entonces comenzamos por buscar una forma de automatizar las tareas que se realizarían frecuentemente, comenzamos con los listados, la aplicación contenía listados, donde una característica mínima que esperábamos de los mismos, era la posibilidad de paginación, para crear una experiencia de usuario aceptable y para no comprometer la performance de nuestro servidor de base de datos. De esta primera necesidad surgió la idea de reutilización de código y de encapsulamiento de toda la lógica de paginación. En este articulo comentamos como el uso de clases en PHP nos ayudó a resolver nuestro problema.

La intención que tuvimos fue la de crear una clase de paginación de forma de que en cada página donde se utilizara la clase, solo debiera definirse como se deseaba realizar el paginado y luego se le pidiera al objeto que mostrará el paginador y como es de suponerse, pudiera pedirsele el conjunto de registros correspondiente a la página seleccionada.
Nuestra clase, que llamamos pagedQuery quedo constituida por un constructor y algunos métodos, detallaremos que hace cada uno de ellos a continuación:

El constructor

El constructor toma los parámetros

p_uid
p_pagesize
p_cnn
p_table
p_preservepagenumber (opcional)
p_customSqlSentence (opcional)

Dónde:

p_uid es una cadena de texto con un nombre de identificación, este nombre interviene en los nombres e id’s de elementos HTML y código javascript que renderizará el paginador, por ejemplo si le pasamos un p_uid llamado “Usuarios” el objeto creará un input hidden llamado PgUsuarios y una función javascript llamada GTP_Usuarios para uso interno. De esta forma, si se desean ubicar varios paginadores dentro de una misma pagina PHP, simplemente hay que asignarle distintos p_uid’s y todo funcionará sin que un paginador interfiera con los otros.

p_pagesize indica la cantidad de registros por página que se mostrarán.

p_cnn espera una conexión a MySql para la toma de datos

p_table indica el nombre de la tabla de la cual se tomarán los datos a mostrar

p_preservepagenumber que es opcional y por defecto se encuentra establecido en verdadero, indica si se debe mantener el número de página en variables de sesión, una opción muy útil para mantener la página seleccionada aunque el usuario ingrese en otras páginas, un caso que se dará sin dudas en los listados editables (o sea en listados desde donde el usuario podrá crear, modificar y eliminar registros).

p_customSqlSentence que por defecto se encuentra establecido como un string vacio y es opcional, tiena la función de indicar que sentencia SQL se debe emplear como fuente de datos (por ejemplo si se debe emplear más de una tabla o si se desea ordenar por algún criterio), en tal caso se omitirá el parámetro p_table, una consideración que se debe tomar en cuenta es que en la consulta pasada en p_customSqlSentence el paginador siempre deberá insertar el comando LIMIT de MySql, a fin de paginar lo más eficientemente posible. por defecto el comando LIMIT es insertado al final de la sentencia SELECT pasada por el usuario, pero si por algún motivo se desea especificar dónde el comando LIMIT debe establecerse, deberá escribirse el texto {INSERT LIMIT HERE} dentro de la sentencia que el usuario establece en p_customSqlSentence para que el paginador haga el reemplazo correspondiente.

La tarea del constructor es guardar los parámetros pasados y recuperar el número de página, (o establecerlo si la página se carga por primera vez, en ese caso se establecerá en la primer página o en la que se indique en una variable interna de sesión ulilizada por la clase, si la opción p_preservepagenumber se ha establecido).

Un punto importante a considerar es que en el costructor se renderizan algunos elementos html y javascripts necesarios para el control del paginado, por lo que es imprescindible que el constructor sea invocado dentro del formulario <form> para que funcione correctamente.

Ejemplos:

$pagedDataset = new pagedQuery(‘Usuarios’, 20, $cnx, ‘tblUsuario’);

$pagedDataset = new pagedQuery(‘Usuarios’, 20, $cnx, ”, true, ‘SELECT nombre FROM tblUsuario WHERE idusuario > 100′ );

$pagedDataset = new pagedQuery(‘Usuarios’, 20, $cnx, ”, true, ‘SELECT nombre FROM tblUsuario WHERE idusuario > 100 {INSERT LIMIT HERE}’ );

El método getRecords

El método getrecords devolverá los registros correspondientes a la página seleccionada.

Ejemplo:

$DataSetUsu = $pagedDataset->getRecords();
$DataRowUsu = mysql_fetch_assoc($DataSetUsu);

El método renderPager

La invocación al método renderPager renderizará la porción de elementos html necesaria para poder efectuar el paginado.

Ejemplo:

$pagedDataset->renderPager();

El método getPageNumber

El método getPageNumber devolverá cual es el número de página seleccionado.

Ejemplo:

$SelectedPg = $pagedDataset->getSelectedPage();

El método getPageCount

El método getPageCount devolverá la cantidad de páginas totales

Ejemplo:

$Pages = $pagedDataset->getPageCount();

La propiedad (variable pública de la clase) CSSClass

La propiedad CSSClass Permite asignarle el nombre de un estilo deseado al paginador y también leerlo.

Para asignar los estilos adecuadamente hay que considerar la estructura del paginador renderizada, la cual se muestra a continuación:

<table class="pager">
<tr>
<td>
<input type="submit" value=" 1 " onclick="return GTP_paginador_adelanto(0);" />
</td>
<td>
<input type="submit" value="2" onclick="return GTP_paginador_adelanto(1);" />
</td>
<td>
<input type="submit" value="3" onclick="return GTP_paginador_adelanto(2);" />
</td>
<td>
<span>Pág. [1/3] - 6 Registros - viendo 2 registro(s) por página</span>
</td>
</tr>
</table>

Entonces si se desea modificar el estilo de los botones de las páginas una opcion válida es definir el estilo

.pager tr td input
{
font-size:20px;
}

otros ejemplos:

.pager
{
padding:10px;
}

.pager tr td
{
background-color:red;
}

.pager tr td input
{
font-size:20px;
}

pager tr td span
{
visibility: hidden;
}

Ejemplo:

$pagedDataset->CSSClass = 'pager';

si no se asigna ninguna clase de estilos, el paginador tomará por defecto el estilo Pager_ + p_uid, para nuestro ejemplo donde p_uid es “Usuarios” el paginador utilizará el estilo Pager_Usuarios por defecto.

Soporte en Javascript

La clase renderizará una serie de funciones javascript por si se desea interactuar con el paginado desde el cliente, todas las funciones javascript terminarán con el postfijo p_uid, Supongamos para nuestro ejemplo que nuestro p_uid es “Usuarios” entonces la clase
renderizará las funciones:

getPageNumberUsuarios, que permite conocer el número de página seleccionado del objeto con p_uid “Usuarios”

getPageCountUsuarios, que permite conocer el número total de páginas del objeto con p_uid “Usuarios”

goToPageUsuarios(número de página), provocará que la página se envié al servidor y vuelva al cliente en la página solicitada en la función del objeto con p_uid “Usuarios”.

Esperamos que esta pequeña contribución les pueda ser de ayuda y a continuación dejaremos una página PHP con la clase y otra con algunos ejemplos

Para que el ejemplo pueda ejecutarse deberá crearse en MySql, en una base de datos que se desee, una tabla que deberá llamarse tblUsuario con dos campos VARCHAR, nombre y apellido y deberán establecerse los datos de la conexión en la página Ejemplos.php en a través de las variables $ej_hostname_cnx (nombre del servidor MySql), $ej_database_cnx (nombre de la base de datos), $ej_username_cnx(nombre de usuario) y $ej_password_cnx(clave del usuario) que se encuentran al principio de la página.

VN:F [1.7.3_972]
Rating: 8.0/10 (8 votos cast)

Ya salió la versión RTM de Silverlight 2.0

Viernes, 17 Oct, 2008 @ 13:16 | Por Gustavo Cantero (The Wolf) | Silverlight

El 13 de Octubre Microsoft anunció, a través de un comunicado de prensa, la versión RTM de Silverlight 2.0, la cual está disponible para descargarse desde el 14 de octubre desde la página de Silverlight.

Como novedad para los diseñadores, el Microsoft Blend 2.5 (que aún no estaba en su versión final) ahora es un Service Pack para el Microsoft Blend 2.0, o sea, para trabajar con Silverlight 2 solamente hay que instalarle el Service Pack 1 al Blend 2.0.

Para los desarrolladores: antes de instalar el Silverlight 2 tienen que instalar el Service Pack 1 para Visual Studio 2008 o, para la gente que no posee una licencia de Visual Studio 2008, Microsoft extendió el soporte a Visual Web Developer 2008 Express Edition para que también se pueda desarrollar aplicaciones para Silverlight 2 con esta herramienta, la cual es de gratis descarga.

Otra noticia importante es que existe un proyecto Open Source llamado eclipse4SL de Soyatec y Microsoft para poder crear aplicaciones Silverlight desde eclipse, el cual planean tenerlo terminado para la segunda mitad de 2009, aunque ya puede descargarse la versión Milestone 1.

Algunas de las novedades que se muestran en el comunicado de prensa son:

  • Soporte para .NET Framework con una librería de clases enriquecida, la cual es un subconjunto compatible de .NET Framework.
  • Controles potentes, entre los que se incluyen: DataGrid, ListBox, Slider, ScrollViewer, Calendar y más.
  • Suporte de plantillas y skins avanzados, lo que facilita la personalización del “look and feel” de nuestras aplicaciones.
  • Deep zoom. Esto permite tener interactividad y navegación con imágenes de ultra-alta resolución.  Esta tecnología es similar a la utilizada en Virtual Earth y Google Map para ir “bajando” en un mapa hasta llegar cerca del suelo.
  • Amplio soporte para networking, permitiendo hacer llamadas a través de REST, WS*/SOAP, POX, RSS y servicios HTTP estándar, permitiendo crear aplicación que se integren fácilmente a los sistemas existentes.
  • Soporte del lenguaje .NET Framework para varios lenguajes, entre los cuales se incluyen Visual Basic, C#, JavaScript, IronPython y IronRuby.
  • Protección avanzada de contenidos, gracias a Silverlight DRM que utiliza PlayReady.
  • Mejora en la escalabilidad, utilizando capacidades de streaming y descarga progresiva, técnicas de búsqueda avanzadas, y soporte para “in-stream advertising”.
  • Soporte multi-plataforma y multi-navegador, con soporte para Mac, Windows y Linux en Firefox, Safari e Internet Explorer.

Como último comentario cabe mencionar que la adopción de Silverlight sigue creciendo, con una penetración cerca del 50% en algunos países, y teniendo decenas de miles de aplicaciones desarrolladas con esta tecnología.  Durante los 17 días de los juegos olímpicos de Beijing el sitio NBColympics.com, el cual utiliza tecnología Silverlight, tuvo más de 50 millones de visitantes, con 1.3 mil millones de páginas visitadas, 70 millones de streams de videos y 600 millones de minutos de video vistos.

A continuación dejo algunos enlaces de utilidad:

Sin duda es una buena noticia para la comunidad desarrolladora.

VN:F [1.7.3_972]
Rating: 7.0/10 (1 voto cast)

Distintas versiones de Internet Explorer en la misma PC

Lunes, 13 Oct, 2008 @ 12:31 | Por Gustavo Cantero (The Wolf) | Navegadores

Como desarrolladores de aplicaciones web muchas veces nos vemos en la necesidad de testear nuestros sitios con distintos navegadores como Internet Explorer, Firefox, Safari, Google Chrome, etc., pero el mayor inconveniente es cuando debemos hacer las pruebas con distintas versiones del mismo navegador, por ejemplo, con Internet Explorer 6 e Internet Explorer 7, ya que al instalar la nueva versión de este producto se pisa la anterior. Uno de los principales motivos de probar nuestros desarrollos con Internet Explorer es que aún hay muchos usuarios que utilizan este navegador, y este tiene algunas diferencias escenciales con el 7, por ejemplo, no soporta las transparencias de los PNG, teniendo que utilizar filtros en los CSS para solucionar esto.
Volviendo al tema de las versiones, para poder instalar distintas versiones del Internet Explorer en nuestro PC la empresa TredoSoft desarrolló un paquete que instala los siguientes productos:

  • Internet Explorer 3.0
  • Internet Explorer 4.01
  • Internet Explorer 5.01
  • Internet Explorer 5.5
  • Internet Explorer 6.0

Cabe mencionar que todas estas versiones pueden convivir juntas y con el Internet Explorer 7.0.

La dirección de donde se pueden bajar este paquete es http://tredosoft.com/Multiple_IE.

VN:F [1.7.3_972]
Rating: 0.0/10 (0 votos cast)

Warriors of the net – IP for the peace

Jueves, 09 Oct, 2008 @ 14:30 | Por Gustavo Cantero (The Wolf) | Redes

Este video de la empresa Cisco (no comercial) explica de forma gráfica, muy sencilla y en español el funcionamiento de las redes, las capas OSI, el envio de paquetes, los routers y switchs, etc.

VN:F [1.7.3_972]
Rating: 10.0/10 (1 voto cast)

Arreglos en SQL Server

Viernes, 03 Oct, 2008 @ 17:18 | Por Dario Krapp | .NET, SQL Server 2008

El uso de arreglos (o arrays) es tan necesario en ciertas ocasiones que ha hecho que su aparición sea tan inmediata como los primeros lenguajes declarativos, y hasta la actualidad, los lenguajes más modernos no los han descartado.

mov [BX],AX; Assember
array = (int *)malloc (N*sizeof(int)); C
type Int_Buffer is array (1..10) of Integer; Adda
int *iarray;iarray = new int [10]; C++
DIM vars$(52) Basic
Dim c(3 to 82) as Integer Microsoft Visual Basic
int[] numbers = new int[10]; C#

Entonces ¿podrían ser los arreglos tan necesarios en el T-SQL de Sql Server? La respuesta es que depende para quien, como suelen ser las respuestas a casi todas las preguntas formulables. Quizás esta necesidad no se manifieste tanto dentro del propio motor de base de datos, ya que siempre es posible acceder a los datos necesarios de alguna u otra forma, pero dentro del mundo de los desarrolladores la necesidad es innegable, como lo veremos con un ejemplo posteriormente. Claro que todo este preámbulo no tendría sentido si el T-SQL de Sql Server tuviese arreglos, pero de hecho no los posee (dejemos de lado por ahora la llegada de SQL Server 2008). ¿Entonces como se puede resolver este problema?, plantearemos entonces el siguiente caso que es, como acostumbramos, un caso del mundo real.

Hace un tiempo desarrollamos una aplicación que empleaba SQL Server 2005 como gestor para el soporte de datos. No tardó en aparecer la siguiente situación, entre nuestras tablas se daba, por las reglas de negocios el caso de que cuando se daba de alta un registro en una tabla del tipo “cabecera” debían asociársele unos cuantos registros del tipo “detalle” conjuntamente. Supongamos el caso de las tablas que se muestran a continuación que representan la estructura del problema abstrayendo complicaciones adicionales.


En nuestro caso estábamos empleando C# sobre el Framework 3.5 y ADO.NET para que nuestra capa de datos accediera al motor de base de datos SQL Server 2005, ante el problema analizamos las siguientes opciones;

  1. Emplear procedimientos almacenados (haciendo tantas llamadas como fueran necesarias).
  2. Emplear consultas dinámicas.
  3. Emplear un procedimiento almacenado que resolviera todo de alguna forma.

Recordemos que nuestro problema a resolver era que cuando se creaba un registro cabecera, en conjunto se creaban siempre varios registros detalle.

La primera aproximación fue la más inocente, poseer un procedimiento almacenado que inserta una cabecera y otro que inserta un detalle, llamémoslos ins_cabecera e ins_detalle para darle nombres intuitivos, entonces si había que crear una cabecera con, por ejemplo, 10 detalles. La metodología era llamar a los procedimientos almacenados desde nuestra capa de datos en .NET de la siguiente forma

ins_cabecera “datos cabecera”
ins_detalle “datos detalle 1″
ins_detalle “datos detalle 2″
ins_detalle “datos detalle 3″
ins_detalle “datos detalle 4″
ins_detalle “datos detalle 5″
ins_detalle “datos detalle 6″
ins_detalle “datos detalle 7″
ins_detalle “datos detalle 8″
ins_detalle “datos detalle 9″
ins_detalle “datos detalle 10″

Claro está que funciona perfectamente, pero hay algo que no se ve muy bien y es cada vez que se llama a una inserción en la capa de datos de .NET se produce una larga cantidad de llamadas contra el motor de base de datos, a pesar de ser procedimientos almacenados, claro está que hay una gran pérdida de tiempo. Inmediatamente se pensó que había que evitar hacer tantas llamadas.

Una segunda opción, (que creo que la descartamos incluso desde antes que a alguien se le ocurriera mencionarla), es la del uso de consultas dinámicas. Básicamente es la idea de escribir una larga sentencia T-SQL y pasársela al servidor de una sola vez para que el mismo la ejecute, este tipo de solución haría una sola llamada, claro está, pero hay muchas desventajas al usar consultas dinámicas en contrapartida a procedimientos almacenados.

En SQL Server (y estoy convencido que en cualquier otro motor de base de datos también), el uso de procedimientos almacenados ofrece ventajas en contraste a las consultas dinámicas, un factor clave es por ejemplo la compilación del plan de trabajo (la estrategia que empleará el motor de base de datos para acceder a los datos) que es reutilizada en procedimientos almacenados y no tiene que recalcularse cada vez que se lanza la consulta, un factor que suma puntos a la hora de evaluar la performance.

Además el uso de procedimientos almacenados asegura la inmunidad contra ataques por injection, como ya sabemos es posible crear consultas dinámicas parametrizadas, pero queda en manos de l del programador utilizarlos, quien puede omitir esta práctica y armar las sentencias por simple concatenación, quedando el sitio vulnerable a ataques por injection, (aunque parezca una locura, claramente este problema es más común de lo que uno se imagina), en cambio, en las llamadas a procedimientos almacenados no existe la posibilidad de dejar esa puerta de entrada. Finalmente otra ventaja es que los procedimientos almacenados pueden invocarse unos a otros, lo cual puede permitir atomizar y reutilizar código, otro punto a favor (Exceptuando lamentablemente nuestro caso en SQL Server 2005 donde la opción no está disponible) los procedimientos almacenados pueden depurarse con mucha facilidad, estos son a mi parecer motivos suficientes para desterrar a las consultas dinámicas, aunque hay situaciones en que no es posible utilizar procedimientos almacenados, por ejemplo existen aplicaciones que crean, modifican y destruyen estructuras de datos dentro del motor de base de datos empleando instrucciones DDL, en éstos casos el acceso a datos posiblemente deberá llevarse a cabo mediante consultas dinámicas. Esperemos que los programados sean cautos en esos casos delicados.

Entonces nos quedó nuestra última carta emplear una sola llamada a un procedimiento almacenado que resolviera todo el problema. Siendo algo así como la solución mágica, la misma consideraría pasarle a nuestro procedimiento almacenado, llamémoslo ins_cabdet, los datos de la cabecera y una estructura con todos los detalles (ya que no sabemos cuántos puede haber) para que los inserte juntos ins_cabdet de una sola vez.

Nuestra función desde la capa de datos en .NET tomaría la siguiente forma:

ins_cabdet “datos cabecera”, Estructura(“datos detalle 1″, “datos detalle 2″ … “datos detalle 10″)

Pero aquí mismo es donde aparece el problema, un arreglo nos vendría más que bien en este caso, pero ya dijimos que SQL Server no soporta arreglos (para quien recién halla cambiado de canal y se haya perdido el principio, recordemos que estamos omitiendo SQL Server 2008). Claramente ahora deberíamos pensar que los arreglos si eran tan necesarios el T-SQL de Sql Server. De todas formas no todo estuvo perdido, el problema pudimos resolverlo desde otro lugar algo lejano. Una de las características que posee SQL Server 2005 es el uso de XML, SQL Server 2005 posee un tipo de datos XML y es capaz de parsear XML’s empleando incluso XPath, entonces la idea fue clara. si no podemos pasarle arreglos al SQL Server intentemos pasarle un parámetro XML con los datos necesarios para poder realizar las inserciones, el XML es flexible y fácil de usar por lo que este tipo de solución es adaptable a muchos casos similares. El precio es claramente la creación del XML desde .NET y el parseo desde SQL Server, pero hoy día ya casi nada es gratis.

Debimos diseñar primero un formato de XML, creamos algo similar a lo siguiente:

<r>
<h nm="nombre_cab" />
 <dc>
  <d nm="nombre_de1"/>
  <d nm="nombre_de2"/>
 </dc>
</r>

Con nuestro fragmento XML armamos un procedimiento almacenado, al que llamamos Ins_CabDet el cual haría lo siguiente:

  • Extracción de cabecera del xml
  • Inserción de cabecera
  • Para cada detalle del xml
    • Extracción del detalle del xml
    • Inserción del detalle

En el mundo real la función tomó la siguiente forma:

CREATE PROCEDURE Ins_CabDet
 @doc xml
AS
BEGIN
 BEGIN TRY
  DECLARE @docHandle int
  EXEC sp_xml_preparedocument @docHandle OUTPUT, @doc
  SET TRANSACTION ISOLATION LEVEL SERIALIZABLE;
  BEGIN TRAN
  INSERT INTO Cabecera (Nombre)
  SELECT nm 'Nombre'
  FROM
  OPENXML (@docHandle,'r/h',1)
   WITH (
    nm nvarchar(20)
   )
  DECLARE @IDCabecera int
  SET @IDCabecera = @@IDENTITY
  INSERT INTO Detalle (IDCabecera, Nombre, OtroDato1, OtroDato2)
  SELECT @IDCabecera 'IDCabecera', nm 'Nombre', od1 'OtroDato1', od2 'OtroDato2'
  FROM
  OPENXML (@docHandle,'r/dc/d',1)
   WITH (
    nm nvarchar(20),
    od1 int,
    od2 datetime
   )
  COMMIT
  RETURN 0
 END TRY
 BEGIN CATCH
  IF @@TRANCOUNT > 0
   ROLLBACK
  RETURN -1
 END CATCH
END

Básicamente hace lo esperado, parsea el XML y lo inserta en las tablas adecuadamente.

Finalmente quedaba un último punto, efectuar la llamada desde la capa de datos en .NET, escribimos el código necesario (en nuestro caso estábamos empleando VS2008) para armar el XML y llamar al procedimiento almacenado, creamos una función similar a la siguiente:

public void Ins_CabDet(string Cab_Nombre, List Detalles) {
 using (SqlCommand objSCmd = new SqlCommand("Ins_CabDet", new SqlConnection("Data  Source=XXX...."))) {
  objSCmd.CommandType = CommandType.StoredProcedure;
  objSCmd.Connection.Open();
  MemoryStream objMS = new MemoryStream();
  using (XmlWriter objXMLW = XmlWriter.Create(objMS))
  {
   objXMLW.WriteStartElement("r");
   objXMLW.WriteStartElement("h");
   objXMLW.WriteAttributeString("nm", Cab_Nombre);
   objXMLW.WriteEndElement();
   objXMLW.WriteStartElement("dc");
   foreach (Detalle objDet in Detalles) {
    objXMLW.WriteStartElement("d");
    objXMLW.WriteAttributeString("nm", objDet.Nombre);
    objXMLW.WriteAttributeString("od1", objDet.OtroDato1.ToString());
    objXMLW.WriteAttributeString("od2", objDet.OtroDato2.ToString("yyyyMMdd",     CultureInfo.InvariantCulture));
    objXMLW.WriteEndElement();
   }
   objXMLW.WriteEndElement();
   objXMLW.WriteEndElement();
   objMS.Position = 0;
   objSCmd.Parameters.Add("@doc", SqlDbType.Xml).Value = new SqlXml(objMS);
  }
  objSCmd.ExecuteNonQuery();
 }
}

Detalle es la estructura:


public struct Detalle {
 public string Nombre { set; get; }
 public int OtroDato1 { set; get; }
 public DateTime OtroDato2 { set; get; }
}

Con esta simple solución pudimos resolver el problema. Un punto a considerar es que en realidad no estamos pasando arreglos, de alguna forma estamos adaptando algo que fue pensado para otra cosa, como una especie de artificio para conseguir nuestro fin, por lo que este tipo de solución no es una solución universal y debería evaluarse en qué casos simplifica y en que otros complica el problema a resolver. Otra opción que no mencionamos es crear un tipo de datos y un procedimiento CLR, aprovechando las ventajas de poder ejecutar código manejado dentro del motor SQL Server 2005, esta es una opción válida que dependiendo del caso también podría implementarse.

Arreglos en SQL 2008

La versión 2008 de Sql Server ha resuelto el problema, finalmente hay arreglos disponibles, aunque los arreglos en T-SQL poseen una visión más cercana al manejo de tablas que al manejo de arreglos de los lenguajes de programación a los que los desarrolladores estamos acostumbrados, esto no provoca ningún problema. La idea aplicada al caso anterior es la siguiente;

suponiendo que seguimos teniendo las mismas tablas Cabecera y Detalle, lo primero que vamos a hacer es crear un tipo de datos de usuario, en SQL Server 2008 no hay que escribir código CLR, la forma de hacerlo es simplemente escribir un script, para nuestro caso el siguiente:


create type TipoDetalle as table
(
 Nombre nvarchar(20),
 OtroDato1 int,
 OtroDato2 DateTime
)

Algo bastante similar a nuestra estructura en C#, una vez creado el tipo de datos ya podemos pasar a crear el procedimiento almacenado


CREATE PROCEDURE Ins_CabDet
(
 @Nombre_Cab nvarchar(20),
 @Detalles TipoDetalle readonly
)
AS
BEGIN
 BEGIN TRY
  SET TRANSACTION ISOLATION LEVEL SERIALIZABLE;
  BEGIN TRAN
  INSERT INTO Cabecera (Nombre) VALUES (@Nombre_Cab)
  DECLARE @IDCabecera int
  SET @IDCabecera = @@IDENTITY
  INSERT INTO
  Detalle (IDCabecera,Nombre, OtroDato1, OtroDato2)
  SELECT @IDCabecera ,Nombre, OtroDato1, OtroDato2
  FROM
   @Detalles;
  COMMIT
  RETURN 0
 END TRY
 BEGIN CATCH
  IF @@TRANCOUNT > 0
   ROLLBACK
  RETURN -1
 END CATCH
END

Claramente puede verse en T-SQL un arreglo es visto como una especie tabla, en cada fila de la tabla hay un elemento (del tipo Detalle en nuestro caso, para eso primero debimos crearlo) y
cada columna representa una propiedad del elemento (Nombre, OtroDato1, etc.), como mencionamos previamente, es un poco diferente a lo usual, pero no puede negarse que es una visión bastante razonable para un motor de base de datos.

Finalmente queda ver como efectuar la llamada desde .NET, en C# la llamada tomaría la siguiente forma:

public void Ins_CabDet(string Cab_Nombre, List Detalles) {
 DataTable dt = new DataTable();
 dt.Columns.Add("Nombre", typeof(string));
 dt.Columns.Add("OtroDato1", typeof(int));
 dt.Columns.Add("OtroDato2", typeof(DateTime));
 foreach (Detalle objDet in Detalles) {
  DataRow dr = dt.NewRow();
  dr[0] = objDet.Nombre;
  dr[1] = objDet.OtroDato1;
  dr[2] = objDet.OtroDato2;
  dt.Rows.Add(dr);
 }
 using (SqlCommand objSCmd = new SqlCommand("Ins_CabDet", new SqlConnection("Data  Source=XXX...."))) {
  objSCmd.CommandType = CommandType.StoredProcedure;
  objSCmd.Connection.Open();
  objSCmd.Parameters.Add("@Nombre_Cab", SqlDbType.NVarChar, 20).Value = Cab_Nombre;
  objSCmd.Parameters.AddWithValue("@Detalles", dt);
  objSCmd.ExecuteNonQuery();
 }
}

Un último detalle es que desde en el código C# puede verse como el arreglo es preparado como una tabla, mapeando la estructura que el motor espera recibir.

SQL Server 2008 trae muchas otras novedades como los campos Date (sin Time) y Time (sin Date), tipos de datos espaciales, la sentencia MERGE y otras cosas que seguramente veremos en algún otro artículo si es que las hemos utilizado para resolver algún problema.

VN:F [1.7.3_972]
Rating: 8.9/10 (12 votos cast)