Vamos a conocer alguna de las clases previstas en .NET para el almacenamiento
de datos de la instancia. Hay muchas opciones así que esto será un breve repaso
a los mecanismos comunes de serialización y de la clase DataSet de ADO.NET.
DataSets
ADO.NET
es el modelo de .NET para el acceso a datos. Los datos se almacenan en objetos
DataSet que están desconectados de la base de datos. XML está integrado en el modelo,
ya que los objetos DataSet se pueden serializar y deserializar fácilmente a
partir de XML.
A
continuación vamos a rellenar un control DataGrid con datos almacenados en un
DataSet, en la parte de arriba se ha definido la estructura del conjunto de
datos en tiempo de ejecución,
(conjunto
de datos sin tipo) a continuación
definiremos la estructura del conjunto
de datos en tiempo de diseño.
Es
necesario crear los siguientes objetos:
-DataAdapter y DataConnection son
objetos que proporcionan los métodos para mover datos entre un DataSet y una
base de datos o fuente de datos.
- El
objeto DataSet es un contenedor que contiene objetos DataTable
e información sobre las relaciones entre
los objetos DataTable.
- DataTable contiene los datos,
organizados en filas y columnas. A través de métodos se puede acceder a los
datos a través de filas y columnas
Crear la interfaz de usuario
Crearemos un Nuevo
proyecto windows y generamos un formulario de este estilo.
DataGrid: DtgPosiciones
Botón1: Carga Dataset
Public Class Form1
Private
m_posicionesSet As New
DataSet()
Private Sub Form1_Load(ByVal
sender As System.Object, ByVal e As
System.EventArgs) Handles MyBase.Load
Dim
TablaPosiciones As New
DataTable("Posiciones")
m_posicionesSet.Tables.Add(TablaPosiciones)
Dim Columnas As New DataColumn("Columnas",
System.Type.GetType("System.Int32"))
Dim Filas As New DataColumn("Filas", System.Type.GetType("System.Int32"))
TablaPosiciones.Columns.Add(Columnas)
TablaPosiciones.Columns.Add(Filas)
Dim col As
Integer
Dim fil As Integer
For col = 0 To 8
For fil =
0 To 8
Dim newRow As DataRow
= TablaPosiciones.NewRow()
newRow("Columna") = col
newRow("Fila") = fil
TablaPosiciones.Rows.Add(newRow)
Next
Next
End Sub
End Class
Con este código hemos
creado un DataSet de la siguiente forma:
§ DataSet contiene
objetos DataTable.
§ DataTable
añadimos objetos “Columnas” y
“Filas” de tipo DataColumn.
§ DataColumn que
posteriormente instanciaremos con un nombre y un tipo de dato, el nombre lo
usaremos para referirnos a una fila o una columna concretas que usaremos para recuperar datos.
§ DataRow contiene los datos. Conviene asegurarse de que el
tipo de los datos coincide con el tipo definido en cada columna.
Creamos el evento
click para el evento del botón carga sin tipo
Private Sub BtnSin_Click(ByVal
sender As System.Object, ByVal e As
System.EventArgs) Handles BtnSin.Click
DtgPosiciones.DataSource
= m_posicionesSet
DtgPosiciones.DataMember = "Posiciones"
End Sub
El objeto
DataSet se puede usar como si fuera una Base de Datos para cargar a partir de
él el DataGrid
Serialización
La
serialización es el proceso mediante el cual los datos de la instancia se
graban de forma secuencial en un archivo. En caso de serializar varias
instancias estas también se también se guardan secuencialmente.
La
deserialización es el proceso de lectura de esos datos en una instancia de la
clase. Existe un estándar para la escritura y lectura de los bytes. Por ejemplo
XML aunque no es el único estándar, un mapa de bits o un archivo word también
son estándar.
Nosotros
también podemos definir nuestro propio formato de serialización. También se
puede utilizar la serialización para traspasar datos entre aplicaciones.
.NET
proporciona varias clases para tareas de serialización, a continuación vamos a
usar la clase BinaryFormatter y la clase
XmlSerializer. Cada clase tiene sus ventajas y limitaciones.
La
interfaz de usuario de la aplicación que se va a crear se muestra en el
siguiente gráfico:
Crearemos una lista
de las posiciones de las piezas sobre un tablero de ajedrez mediante la
definición de cada posición de la casilla en el tablero como una fila y columna
(x, y). Entonces, el usuario puede guardar la lista de posiciones en forma
binaria o en forma de XML. Una vez que la lista es guardada, el usuario puede recuperar
los datos en un momento posterior para restaurar la lista de posiciones. El
diseño incluye tres clases: CPosicion, CPosPieza, y CColeccionPiezas. La clase CPosPieza contiene dos instancias CPosicion, la posición actual y la siguiente, y
la CColeccionPiezas contiene cero o más casos de piezas sobre el
tablero. La anidación de estas clases nos permitirá ver cómo funciona la serialización y ver las reglas y que se aplican con .NET.
Implementando Serialización Binaria
La Serialización
binaria almacena la instancia de una clase como un flujo de Bytes, este flujo
se puede guardar en un archivo de memoria o transmitirse por la red. Contiene
el estado del objeto, incluyendo los campos privados y públicos de la
instancia.
Se puede
restaurar a través de la interfaz ISerializable, de este modo los datos no son
legibles como texto paro tampoco están encriptados.
Crear las clases de datos
El modelo
de datos de esta aplicación incluye las clases: CPosicion, CPosPieza y CColeccionPiezas. La interfaz
de usuario contiene métodos para crear, eliminar, guardar y cargar las
instancias creadas.
Crear una nueva aplicación
de Windows llamada Serializacion.
Añadir una nueva clase al
proyecto llamada CPosicion. Y
añadir las propiedades Columna y Fila a la clase.
Public Class CPosicion
Implements System.Runtime.Serialization.ISerializable
Private m_Columna As String 'Columna
Private m_Fila As Integer 'Fila
Public Property
Columna() As String
Get
Return (m_Columna)
End Get
Set(ByVal Value As String)
m_Columna =
Value
End Set
End Property
Public Property
Fila() As Integer
Get
Return (m_Fila)
End Get
Set(ByVal Value As Integer)
m_Fila =
Value
End Set
End Property
End Class
Añadir un constructor y un
método sobrecargarlo ToString. El método ToString es usado para mostrar las instancias de CPosicion en tiempo de ejecución.
Public Sub New()
End Sub
Public Sub New(ByVal columna As String, ByVal fila As Integer)
m_Columna =
Columna
m_Fila = Fila
End Sub
Public Overrides Function
ToString() As String
Return (String.Format("({0}, {1})", Me.Columna,
Me.Fila))
End Function
Añadir la clase CPosPieza al proyecto
y añadirle dos propiedades para la posición actual y para la siguiente.
'para cada pieza
contiene su posición actual y la siguiente (de todas las posibles la que moverá
finalmente)
Public Class CPosPieza
Private m_posiciones() As CPosicion = {New
CPosicion(), New CPosicion()}
Public Property Posicion() As
CPosicion()
Get
Return
(m_posiciones)
End Get
Set(ByVal Value As CPosicion())
If (Value.Length = 2) Then
m_posiciones = Value
End If
End Set
End Property
End Class
Añadir un constructor y el
método sobrecargado ToString:
Public Sub New(ByVal anterior As
CPosicion, ByVal siguiente As CPosicion)
m_posiciones
= New CPosicion() { anterior, siguiente }
End Sub
Public Overrides Function
ToString() As String
Dim
cadenaposiciones As String
Dim
posicion As Integer
For
posicion = 0 To m_posiciones.Length - 1
cadenaposiciones +=
m_posiciones(posicion).ToString() + " "
Next
Return
(cadenaposiciones)
End Function
Añadir al proyecto una
clase llamada CColeccionPiezas basada en la clase CollectionBase.
Public Class CColeccionPiezas
Inherits System.Collections.CollectionBase
End Class
Añadir los métodos Agregar y Eliminar:
Public Sub Agregar(ByVal
pieza As CPosPieza)
Me.InnerList.Add(pieza)
End Sub
Public Sub Eliminar(ByVal
pieza As CPosPieza)
Me.InnerList.Remove(pieza)
End Sub
Sobrecargar el método ToString y añadirlo a la clase para mostrar las
posiciones de las piezas en un control ListBox:
Public Overrides Function
ToString() As String
Dim posiciones
As String
Dim ActYSig As
CPosPieza 'Contiene
la posición anterior y la siguiente
For Each
ActYSig In Me.InnerList
posiciones += ActYSig.ToString() &
ControlChars.CrLf
Next()
Return (posiciones)
End Function
Public Function ToArray() As
Object()
Dim posiciones(Me.Count
- 1) As Object
Dim ActYSig As Integer
For ActYSig = 0 To Me.Count - 1
posiciones(ActYSig) = InnerList(ActYSig)
Next
Return (posiciones)
End Function
Después añadiremos el
interfaz de usuario para cargar la instancia de datos.
Crear el interfaz de usuario
Abrir el Form1 y añadir
los controles listados en la siguiente tabla, poniendo
las propiedades indicadas.
Control
|
Propiedad
|
Valor
|
CheckedListBox
|
Name
|
chkPuntosSeleccionados
|
Label
|
Text
|
Seleccionar
dos posiciones
|
ListBox
|
Name
|
lstListaPosiciones
|
Label
|
Text
|
Posiciones
|
Button
|
Name
|
btnAnadirPosición
|
Text
|
Añadir
|
|
Button
|
Name
|
btnQuitarPosicion
|
Text
|
Eliminar
|
|
Button
|
Name
|
btnLimpiarTodo
|
Text
|
Limpiar
todo
|
|
Button
|
Name
|
btnSalvarBinario
|
Text
|
Salvar
Binario
|
|
Button
|
Name
|
btnCargarBinario
|
Text
|
Cargar
Binario
|
|
Button
|
Name
|
btnSalvarXML
|
Text
|
Salvar
XML
|
|
Button
|
Name
|
btnCargaXML
|
Text
|
Cargar
XML
|
Hacer doble click sobre el
formulario para añadir el siguiente código que rellena la checkedListBox con
posiciones.
Private Sub Form1_Load(ByVal
sender As System.Object, ByVal e As
System.EventArgs) Handles MyBase.Load
Dim columnas As Integer
Dim filas As Integer
Dim descol As String
For columnas = 1 To 8
Select Case columnas
Case 1
descol = "TD"
Case 2
descol = "CD"
Case 3
descol = "AD"
Case 4
descol = "D"
Case 5
descol = "R"
Case 6
descol = "AR"
Case 7
descol = "CR"
Case 8
descol = "TR"
End Select
For filas = 1 To 8
Me.chkPuntosSeleccionados.Items.Add(New CPosicion(descol, filas))
Next
Next
End Sub
Creamos los
eventos para el botón Añadir al haciendo doble click sobre el botón del
formulario. Agregamos un campo para la clase CColeccionPiezas y agregamos
el código para el evento de añadir una nueva posición con una instancia de CColeccionPiezas. La
propiedad checkedItemCollection es una propiedad del control CheckedListBox que
contiene una colección de todos los elementos que se han marcado. Estos objetos
se devuelven como Instancias de System.Object, por lo
que debe convertir de nuevo a CPosicion una instancia de una pieza nueva con ellos.
Private
m_pospiezas As New
CColeccionPiezas()
Private Sub btnAnadirPosicion_Click(ByVal
sender As System.Object, ByVal e As
System.EventArgs) Handles
btnAnadirPosicion.Click
Dim checkedPosicionesClicadas As
CheckedListBox.CheckedItemCollection = Me.chkPuntosSeleccionados.CheckedItems
If checkedPosicionesClicadas.Count = 2 Then
m_pospiezas.Agregar(New CPosPieza(CType(checkedPosicionesClicadas(0),
CPosicion), CType(checkedPosicionesClicadas(1),
CPosicion)))
lstListaPosiciones.Items.Clear()
lstListaPosiciones.Items.AddRange(m_pospiezas.ToArray())
Dim item As Integer
For Each item In chkPuntosSeleccionados.CheckedIndices
chkPuntosSeleccionados.SetItemChecked(item, False)
Next
Else
MessageBox.Show("Debes agregar exactamente dos posiciones.")
End If
End Sub
Creamos
el controlador de eventos Click para el botón Eliminar. Añadimos el siguiente
código para eliminar las dos posiciones de la
pieza seleccionada de m_pospiezas. Como con
la propiedad CheckedItemCollection de la lista CheckedListBox, la Propiedad
SelectedItem del ListBox devuelve
una referencia a System.Object. Tendremos
hacer un cast a la instancia de CPosPieza antes de llamar al método
Eliminar de la instancia m_pospiezas de CColeccionPiezas.
Private Sub btnQuitarPosicion_Click(ByVal
sender As System.Object, ByVal e As
System.EventArgs) Handles
btnQuitarPosicion.Click
If lstListaPosiciones.SelectedIndex <> -1 Then
m_pospiezas.Eliminar(CType(lstListaPosiciones.SelectedItem,
CPosPieza))
lstListaPosiciones.Items.Clear()
lstListaPosiciones.Items.AddRange(m_pospiezas.ToArray())
End If
End Sub
Crear el evento Click para
el botón Limpiar todo. Añadir código para eliminar todas las instancias de
Posiciones de m_pospiezas.
Private Sub btnLimpiarTodo_Click(ByVal
sender As System.Object, ByVal e As
System.EventArgs) Handles btnLimpiarTodo.Click
m_pospiezas.Clear()
lstListaPosiciones.Items.Clear()
End Sub
Con todas
las clases definidas y sus métodos para crear y eliminar instancias, ahora se
pueden definir los métodos de serialización y deserialización. Esto requerirá
adiciones tanto a la interfaz de usuario y para las clases.
Definir la serialización
Para
utilizar la serialización binaria, es necesario agregar el atributo Serializable para
cada clase que deseemos serializar. Además, si deseamos definir los campos que
se serializan y si están en serie, se puede implementar la interfaz ISerializable. El
gráfico de objetos de
CColeccionPiezas contiene
las clases CPosPieza y CPosicion. CPosPieza y CColeccionPiezas utilizarán la serialización predeterminada
proporcionada por la adición del atributo Serializable. La clase y CPosicion definirá
su propia serialización mediante la implementación del interfaz ISerializable y
agregando el atributo Serializable.
En este paso
se va a serializar la instancia m_pospiezas de CColeccionPiezas, que a
su vez contiene instancias de CPosPieza, que a su vez contienen
instancias CPosicion. Esta dependencia de clases se conoce como objeto gráfico de una
clase. En tiempo de ejecución .NET es capaz revisa los objetos durante la
serialización para garantizar que todas las clases están definidas
correctamente y se pueden serializar.
Añadir el atributo
Serializable y el interfaz ISerializable a la
declaración de la clase CPosicion
<Serializable()> Public
Class CPosicion
Implements
System.Runtime.Serialization.ISerializable
Añadimos
código al procedimiento generado automáticamente GetObjectData como único
miembro del interfaz ISerializable. El primer parámetro, info de tipo SerializationInfo, es una
colección de pares de nombre / valor que pasamos al proceso de serialización.
De esta manera, se puede controlar en qué forma está serializado. Aquí, le sumamos
los valores de m_columna y m_fila a la
colección:
Public Sub GetObjectData(ByVal
info As
System.Runtime.Serialization.SerializationInfo, ByVal
context As
System.Runtime.Serialization.StreamingContext) Implements
System.Runtime.Serialization.ISerializable.GetObjectData
info.AddValue("COLUMNA", m_Columna)
info.AddValue("FILA",
m_Fila)
End Sub
Proporcionamos
un constructor que tome los parámetros SerializationInfo y StreamingContext. Este
método es necesario para la serialización, pero no cumple con la definición de
la interfaz, porque los interfaces no pueden definir constructores. Este
constructor se llama cuando el objeto se deserializa y es necesario cuando se
está implementando la interfaz ISerializable. Para que el constructor pueda
leer los datos, estos deben estar igual que fueron escritos.
Public Sub New(ByVal info As
System.Runtime.Serialization.SerializationInfo, ByVal
context As
System.Runtime.Serialization.StreamingContext)
m_Columna =
info.GetInt32("COLUMNA")
m_Fila = info.GetInt32("FILA")
End Sub
Agregamos el atributo Serialización a
las clases CPosPieza y CColeccionPiezas. Si
utilizamos el valor de
serialización por defecto no son necesarios más cambios.
<Serializable()> Public Class
CPosPieza
<Serializable()> Public Class
CPosPiezaPublic Class CColeccionPiezas.
Serializar y deserializar los datos
Ya sólo queda crear las
instancias BinaryFormatter y FileStream
para serializar los datos de la instancia m_posiciones en un
archivo.
Añadimos una instrucción
Imports al archivo de origen del Formulario. Esto permitirá usar el nombre no
calificado de la clase BinaryFormatter.
Imports
System.Runtime.Serialization.Formatters.Binary
Private
m_FicheroBinario As String
= Application.StartupPath + "\Posicionn_Pieza.dat"
Creamos
el controlador de eventos para los botones Guardar binario y Cargar Binario y
metemos código para serializar el campo m_pospiezas. Los
pasos para serializar son simples: Creamos una secuencia (stream) y un objeto
BinaryFormatter. El método Serialize toma como parámetros el stream serialización
y el objeto que se va a serializar.
Private Sub btnSalvarBinario_Click(ByVal
sender As System.Object, ByVal e As
System.EventArgs) Handles
btnSalvarBinario.Click
Dim stream As New System.IO.FileStream(m_FicheroBinario, System.IO.FileMode.Create)
Dim binary As New BinaryFormatter()
binary.Serialize(stream,
m_pospiezas)
stream.Close()
End Sub
Creamos
el controlador de eventos click para el botón Cargar binario y añadimos código
para deserializar el campo m_pospiezas. Después
de cargar los datos, llenamos el ListBox llamado stListaPosiciones con los datos nuevos.
Private Sub btnCargarBinario_Click(ByVal
sender As System.Object, ByVal e As
System.EventArgs) Handles
btnCargarBinario.Click
Dim stream As New System.IO.FileStream(m_FicheroBinario,
System.IO.FileMode.Open)
Dim binary As New BinaryFormatter()
m_pospiezas = CType(binary.Deserialize(stream), CColeccionPiezas)
stream.Close()
lstListaPosiciones.Items.Clear()
lstListaPosiciones.Items.AddRange(m_pospiezas.ToArray())
End Sub
Utilizamos
la clase BinaryFormatter para serializar, y ahora deserializar el campo m_posiciones. El
método Deserialize toma una instancia stream y devuelve una instancia de
System.Object. Se hace un cast del objeto del tipo que se serializa el stream.
Ejecutar y testear la aplicación
A continuación ejecutamos y probamos la
aplicación. El archivo de datos no existirá hasta que se cree por lo que tendremos que añadir y guardar algunos datos
antes de intentar cargar los datos.
Ejecutar la aplicación, el
listbox de posiciones de las piezas (actual y siguiente) estará vacío.
Seleccionar dos posiciones
y pulsar el botón Añadir. Tendremos una instancia CPosPieza en el
listbox.
Añadir otra instancia CPosPieza.
Pulsar el botón Salvar
Binario. Se salvarán la posición actual y siguiente de dos piezas.
Pulsar el botón limpiar
todo y se borrarán las posiciones guardadas en el punto anterior
Al pulsar el botón Cargar
Binario. Las dos instancias guardadas aparecerán en el list box
Una porción de los datos
binarios se muestran aquí en notepad donde se puede leer un poco, el stream.
Implementando la Serialización XML
XML es similar a HTML sólo
que permite al desarrollador definir etiquetas. Aunque HTML contiene un
conjunto predefinido de etiquetas, como Título y Estilo, los desarrolladores
pueden crear las etiquetas que tienen que definir sus datos, como CPosicion, CPosPieza,CColeccionPiezas.
La serialización XML tiene
estas características:
-Sólo se serializan los campos públicos y
propiedades. Si los datos de la instancia no pueden ser vistos desde los campos
públicos o propiedades, estos no se inicializarán cuando el objeto se
deserializa.
- La serialización XML requiere un constructor
público sin parámetros.
Hay convenciones especiales para la serialización de
clases de colección, como la CColeccionPiezas.
- La salida de la serialización es texto legible,
sin formato.
Serializar y des-serializar los datos
Para
serializar XML, tendremos que agregar los miembros públicos necesarios para la
instancia y establecer sus propiedades.
Añadiremos
un constructor sin parámetros para las
clases CPosPieza y CColeccionPiezas.
Public Sub New()
End Sub
Si queremos serizalizar CColeccionPiezas, como la
clase implementa el interfaz ICollection lo tenemos que hacer a través de
su clase base CollectionBase.
Implementaremos:
- Una propiedad Contar que devuelva un entero. La clase base, CollectionBase,
proporciona la propiedad Count.
- Un método Agregar que
tome un parámetro del mismo tipo que el objeto devuelto por la propiedad actual
del método GetEnumerator.
- Un método Item, El valor
devuelto por este método debe tener el mismo tipo que el parámetro del método Agregar.
Estos miembros permitirán que
el proceso de serialización pueda acceder a todos los objetos de la colección a
través del método Item y deserializar el objeto mediante el método Agregar
Default Public Property Item(ByVal indice As Integer) As CPosicion
Get
Return CType(Me.InnerList.Item(indice), CPosicion)
End Get
Set(ByVal Valor As CPosicion)
Me.InnerList.Item(indice) = Valor
End Set
End Property
Con esto completamos los cambios necesarios para realizar en nuestras clases.
Seguidamente añadimos este
código en el Form1 para que invoque la serialización de XML
Imports
System.Xml.Serialization
Y definimos la cadena
para el XML
Private
m_FicheroXML As String
= Application.StartupPath & "\Posicion_Pieza.xml"
Y el código para el botón SalvarXML
Private Sub btnSalvarXML_Click(ByVal
sender As System.Object, ByVal e As
System.EventArgs) Handles btnSalvarXML.Click
Dim escribir As New System.IO.StreamWriter(m_FicheroXML)
Dim xmlSerial As New XmlSerializer(m_pospiezas.GetType())
xmlSerial.Serialize(escribir,
m_pospiezas)
escribir.Close()
End Sub
Este código serializa
m_pospiezas es similar a los método binarios pero necesita saber el tipo de la instancia a
serializar.
Y el código para el botón cargarXML
Private Sub btnCargarXML_Click(ByVal
sender As System.Object, ByVal e As
System.EventArgs) Handles btnCargarXML.Click
Dim lector As New System.IO.StreamReader(m_FicheroXML)
Dim SerializaXML As New XmlSerializer(System.Type.GetType("Serializar.CColeccionPiezas"))
m_pospiezas
= CType(SerializaXML.Deserialize(lector),
CColeccionPiezas)
lector.Close()
lstListaPosiciones.Items.Clear()
lstListaPosiciones.Items.AddRange(m_pospiezas.ToArray())
End Sub
Ejecutamos y probamos el programa.
Cargar y salvar los datos
Si ahora pulsamos en
cargar binario para cargar los datos almacenados en binario la última vez y
pulsamos el botón salvar XML, limpiamos todo y pulsamos el botón cargar XML.
No hay comentarios:
Publicar un comentario