DataSet
Definición inflexible de tipos

Fecha: 27/Jul/2004 (23/07/2004)
Autor: Ariel N. Menendez

 


 

En esta oportunidad vamos a hablar sobre cómo hacer para que los datasets que utilicemos en nuestra aplicación sean más intuitivos al momento de utilizarlos.

Como logramos esto, fácil, equiparando sus tipos respecto de la entidad con la que trabajan.

 

Índice:

 

 

 

En primer lugar, para poder continuar quiero que quede claro esto de la definición inflexible de tipos.

 

Siempre que utilizamos un dataset para realizar una determinada tarea sobre entidades de la base de datos lo que acostumbramos hacer es crear un DataAdapter invocar una instrucción Transac-SQL o un Procedimiento Almacenado y con esto llenar un objeto cualquiera de tipo DataSet.

 

Ej.:

 


string strConn = "Integrated Security=SSPI; Database=suscriptors; Server=localhost";

 

SqlConnection sCon = new SqlConnection(strConn);

SqlDataAdapter da = new SqlDataAdapter();

 

da.SelectCommand = new SqlCommand("SELECT * FROM Category",sCon);

da.SelectCommand.CommandType = CommandType.Text;

 

DataSet ds = new DataSet();

 

da.Fill(ds,"categories");

 

foreach(DataRow dr in ds.Tables["categories"].Rows)

{

Console.WriteLine(dr["CategoryName"]);               

}

 


Observen el código anterior. Al momento de declarar el dataset que va a contener la entidad category lo que hice fue crear un objeto llamado ds (de tipo DataSet), y luego para recorrer su colección de rows, utilizo la instrucción foreach y digo que haga un recorrido por cada fila de la tabla categories.

 

 

 

Tal vez aún no vean el inconveniente, porque a pesar de todo, este código va a funcionar, pero ¿no les parece que el dataset y la entidad a la que hace mención deberían estar más relacionados? O sea, ¿no seria mejor que el dataset fuese de tipo Category, en este caso, y que en lugar de recorrer los rows de esta forma, pueda realizar un foreach de categories el cual internamente tendría una colección de categoryName?

 

Ok, esto lo voy a responder por ustedes, SI, la respuesta a estas preguntas es sin lugar a duda… SI. No sólo por ser mas intuitivo de utilizar, sino también porque de esta forma podría definir mi propia clase que herede de dataset la cual tenga toda la funcionalidad de un dataset y que a su vez refleje el esquema de las entidades que poseo en mi base de datos.

 

Acabo de nombrar la palabra mágica “Esquema”. Lo que en realidad necesito para poder realizar esto es el esquema con la información necesaria para poder utilizar una herramienta que me va a solucionar la vida, realizando esta clase por mí.

 

Y cómo hago para conseguir el esquema de las entidades que voy a utilizar con mi dataset, fácil.

 

Obtener el esquema de las entidades.

 

Para lograr mi cometido, tengo que escribir unas pocas líneas de código que me permitan conseguir el esquema de las entidades sin necesidad de tener que hacerlo yo mismo a mano, lo cual implicaría ser un buen conocedor de esquemas (XSD). Las pocas líneas de las que hablo tendrían que realizar una conexión al repositorio de datos en cuestión, obtener las entidades que voy a utilizar con el dataset, llenar un dataset cualquiera, y con el método GetXmlSchema() obtener el esquema del dataset que cargue. Una vez obtenido el esquema con este método, tendría que grabar el esquema en un archivo con extensión .XSD; este archivo va a ser el que voy a utilizar para poder generar la clase tipada.

 

Ej.:

 


string strConn = "Integrated Security=SSPI;Database=suscriptors;Server=localhost";

 

SqlConnection sCon = new SqlConnection(strConn);

 

SqlDataAdapter da = new SqlDataAdapter();

da.SelectCommand = new SqlCommand("SELECT * FROM Category",sCon);

da.SelectCommand.CommandType = CommandType.Text;

 

DataSet ds = new DataSet(DSCategory”);

 

da.Fill(ds,"categories");

 

//grabo el esquema en un archivo

StreamWriter sw = new StreamWriter(@"e:\DSCategory.xsd");

sw.WriteLine(ds.GetXmlSchema());

sw.Close();

                 

 

 


Nota: Es Importante que al crear la instancia del DataSet le asignen un nombre porque sino de lo contrario, en el esquema como nombre del dataset, aparecerá NewDataSet y ésto se verá reflejado en el nombre de la clase la cual debería tener un nombre mas intuitivo como por ejemplo el que yo le di “DSCategory”. Y lo mismo al llenar el DataSet con los DataTable, si no le ponen un nombre “categories” les aparecerá en el esquema TableN, donde N es un numero correlativo el cual comienza desde cero (Ej. Table0, Table1,…).

 

 

Una vez que corrimos este pequeño bloque de código, lo que obtendremos como resultado será algo como esto:

 

<?xml version="1.0" encoding="utf-16"?>

<xs:schema id="DSCategory" xmlns="" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">

  <xs:element name="DSCategory" msdata:IsDataSet="true">

    <xs:complexType>

      <xs:choice maxOccurs="unbounded">

        <xs:element name="categories">

          <xs:complexType>

            <xs:sequence>

              <xs:element name="CategoryID" type="xs:int" minOccurs="0" />

              <xs:element name="CategoryName" type="xs:string" minOccurs="0" />

            </xs:sequence>

          </xs:complexType>

        </xs:element>

      </xs:choice>

    </xs:complexType>

  </xs:element>

</xs:schema>

 

Sin más ni menos, el esquema de nuestra consulta, esta reflejando la estructura de nuestra entidad en el modelo de datos.

 

Nota: para poder utilizar este esquema con la herramienta que nos va a pertimir realizar la clase, tenemos que realizar un cambio muy simple. Noten que en el encabezado del esquema se encuentra una Processing Intruction, la cual setea la versión del xml y el encoding que utiliza. Para realizar el proceso de convertir el esquema a clase, se debe cambiar el encoding de utf-16 a utf-8, puesto que la herramienta sólo admite este encoding. De esta forma, al pasar este esquema como parámetro, no habrá ningún problema y lograremos nuestro objetivo.

 

<?xml version="1.0" encoding="utf-8"?>

 

Esta primera etapa ya nos da la posibilidad de comenzar con el proceso de conversión.

 

 

Convertir el esquema a clase

 

Para poder realizar la conversión de la que tanto he hablado hasta el momento, es necesario utilizar una herramienta que ya viene con el framework denominada XSD.exe

 

 

Nota: para poder acceder a ella desde cualquier path les recomiendo que utilicen la consola que viene seteada en el accedo de .net (Start > Programs > Microsoft Visual Studio .Net 2003 >  Visual Studio Tools > Visual Studio 2003 Command Prompt)

 

 

Bueno, una vez dicho esto, manos a la obra.

 

El primer paso que se debe realizar es ubicar el archivo .XSD que he realizado en alguna carpeta que sea fácilmente identificable.

 

Una vez hecho esto, tengo que iniciar la consola (Command Prompt) para poder ejecutar el comando XSD.exe.

 

Dentro de la carpeta donde se encuentra situado el esquema, invoco el comando de la siguiente manera:

 

 

 

 

Una vez invocado, ya poseemos nuestra clase.

 

Dentro de los parámetros que se le pasan a la herramienta, se encuentran:

 

/d à define que la clase generada será de tipo Dataset

/l  à es para determinar bajo qué código será generada la clase. En nuestro clase le definimos /l:CS para que el comando la genere en C#.

 

/n à sirve para que podamos definirle un espacio de nombre a nuestra clase. En este ejemplo, le puse MyDataSets.EntityDataSets.

 

 

Si miramos en la carpeta donde se encontraba el esquema ahora veremos que, además del esquema se encuentra la clase que se generó a partir de su estructura.

 

Una vez obtenida la clase, lo que podemos hacer es compilarla para obtener un asembly el cual referenciar en el proyecto donde necesitemos hacer uso de la entidad en cuestión.

 

 

Compilar la clase

 

Para compilar la clase los pasos son muy simples. Al igual que el comando para crearla, también tenemos el comando para compilarla (esto ya lo tenemos bien claro, supongo). El comando es csc.exe, el cual nos permite compilar clases programadas en C#.

 

 

 

Dentro de los parámetros que podemos pasar a la compilación se encuentran:

 

/t:library à El cual me permite construir una librería.

/r à Permite definir de qué otros assembly depende éste que estoy generando.

 

Una vez realizado estos pasos, ya estamos en condiciones de utilizar nuestra librería en algún proyecto que lo requiera. Por si no lo notaron, en la misma carpeta donde está el esquema y la clase, ahora poseemos un assembly recién horneado y listo para usar.

 

 

Utilizar el assembly en un proyecto

 

El próximo paso que nos resta para finalizar el articulo es mostrar de qué manera funcionaría esto en un proyecto, por lo tanto vamos a crear un proyecto de tipo Console donde podamos utilizar nuestra clase.

 

  1. El primer paso es crear el proyecto. Creen uno con el nombre de MyDataSetTest.
  2. Agreguen el Assembly DSCategory.dll como referencia del proyecto.
  3. Una vez agregada la referencia invoquen al espacio de nombre MyDataSets.EntityDataSets utilizando la directiva Using.

 

Nota: no olviden agregar también los espacios de nombre System.Data y System.Data.SqlClient.

 

 

  1. Una vez hecho esto escriban las siguientes líneas de código.

 

 


string strCon = "Integrated Security=SSPI;Database=suscriptors;server=localhost";

 

SqlDataAdapter da = new SqlDataAdapter("select * from category",strCon);

 

DSCategory ds = new DSCategory();

 

da.Fill(ds,"categories");

 

foreach(DSCategory.categoriesRow cat in ds.categories.Rows)

{

      Console.WriteLine(cat.CategoryID + " --> " + cat.CategoryName);

}

 


Observen que nuestro DataSet ya no es un dataset común, puesto que está tipado al tipo de entidad que maneja. Además observen la forma de iterar sobre las filas, es mucho más natural siendo que se invoca directamente a una colección de filas correspondientes de categories.

Otro detalle a  destacar es que los campos de la entidad se ven reflejados como propiedades de la clase. Por lo tanto también es mucho mas intuitivo y prolijo, y por ultimo y para rematarla, podemos acceder a nos nombres comunes a nuestra entidad de base de datos por medio del intellicense y utilizar su auto complete el cual nos da mucha rapidez al momento de programar.

 

Bueno, espero que este artículo les sea útil. Saludos.

 


Ariel Norberto Menendez
menendea@hotmail.com
www.cardinal-cg.com

Fichero con el código de ejemplo: menendea_DataSetDefinicionInflexibleDeTipos.zip- 9KB


ir al índice