Filtrar las columnas de una tabla y realizar búsquedas en un DataSet [Mejorando el rendimiento de la aplicación y ralizando búsquedas rápidas trabajando en modo desconectado]
Una de las características de esta nueva tecnología .NET es que los desarrolladores podemos construir aplicaciones cada vez más escalables, esto es debido a que podemos trabajar en modo desconectado; es decir, reduciendo al máximo el número de "conexiones abiertas" en la base de datos. Además sabemos que anteriormente se obtenía mejor rendimiento manteniendo una conexión abierta para la conexión a la fuente de datos, esto era utilizando ADO, pero como todo cambia conforme avanza la tecnología y la ciencia, es así que gracias a los famosos DataSets nos damos ese privilegio de tomar una parte de los datos y trabajar con ellos manteniéndolos en memoria en forma relacional. Todo esto se lo debemos a Microsoft por haber considerado en su gran lista de prioridades para ADO .NET lo antes mencionado. Para entender todos estos conceptos, creo conveniente que debe quedar claro algunas cosillas con respecto a ADO .NET. ADO .NET Constituye la interfaz fundamental de las aplicaciones para proporcionar servicios de acceso a datos en la plataforma Microsoft .NET. ADO.NET proporciona acceso coherente a orígenes de datos como Microsoft SQL Server, así como a orígenes de datos expuestos mediante OLE DB y XML. Las aplicaciones para usuarios que comparten datos pueden utilizar ADO.NET para conectar a estos orígenes de datos y recuperar, manipular y actualizar los datos. ADO.NET separa limpiamente el acceso a datos de la manipulación de datos y crea componentes discretos que se pueden usar por separado o conjuntamente. ADO.NET incluye proveedores de datos de .NET Framework para conectarse a una base de datos, ejecutar comandos y recuperar resultados. Esos resultados se procesan directamente o se colocan en un objeto DataSet de ADO.NET con el fin de exponerlos al usuario para un propósito específico, junto con datos de varios orígenes, o de utilizarlos de forma remota entre niveles. El objeto DataSet de ADO.NET también puede utilizarse independientemente de un proveedor de datos de .NET Framework para administrar datos que son locales de la aplicación o que proceden de un origen XML. Las clases de ADO.NET se encuentran en System.Data.dll y se integran con las clases de XML incluidas en System.Xml.dll. Al compilar código que utiliza el espacio de nombres System.Data, haga referencia tanto a System.Data.dll como a System.Xml.dll. ADO.NET proporciona a los programadores que escriben código administrado una funcionalidad parecida a la que ADO proporciona a los programadores de COM nativo. Para obtener una descripción de las diferencias entre ADO y ADO.NET, te invito a ver "ADO.NET para el programador de ADO" en http://msdn.microsoft.com/library/enus/dndotnet/html/ADONETProg.asp. El DataSet de ADO.NET es una representación de datos residente en memoria que proporciona un modelo de programación relacional coherente independientemente del origen de datos que contiene. Un DataSet representa un conjunto completo de datos, incluyendo las tablas que contienen, ordenan y restringen los datos, así como las relaciones entre las tablas. El objetivo de este artículo no es explicar a fondo todo respecto a ADO .NET, sino más bien poner en práctica algunos conceptos arriba explicados. A continuación vamos directo al grano; es decir al ejemplo. Trataré de no ser tan bruto para explicarles lo que implementé en mi preferido lenguaje Visual Basic .NET. Mostraré un ejemplo donde se trabajará con todas la tablas de la base de datos Northwind, donde después de "vaciar" las tablas de la base de datos en una dataset procederemos a visualizar en una grilla tan sólo las columnas que el usuario seleccione. Luego haremos la consulta en la tabla Customers de todos los clientes de acuerdo al país, para nuestro ejemplo será "France".Los resultados se visualizarán en otra grilla.
En la parte baja de este artículo se encuentra el fichero con el código de la aplicación que implementé, donde podrás chekear mejor el programa. A continuación sigue el código en Visual Basic .Net: 'La cadena de conexión se declara en un módulo. Module Module1 Public StringConexion As String = "Data Source=(Local);Initial Catalog=Northwind; Integrated Security=SSPI" End Module Imports System.Data.SqlClient Public Class Form1 Inherits System.Windows.Forms.Form Public Shared MiDataset As New DataSet Public i As Integer = 0 Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load BtnFiltrarColumnas.Enabled = False GroupBox1.Enabled = False 'Establecemos las cadenas de comandos y conexiones para 'acceder a cada una de las tablas de la base de datos Dim Adaptador1 As New SqlDataAdapter("SELECT * FROM Products", StringConexion) Dim Adaptador2 As New SqlDataAdapter("SELECT * FROM customercustomerdemo", StringConexion) Dim Adaptador3 As New SqlDataAdapter("SELECT * FROM customerdemographics", StringConexion) Dim Adaptador4 As New SqlDataAdapter("SELECT * FROM employeeterritories", StringConexion) Dim Adaptador5 As New SqlDataAdapter("SELECT * FROM territories", StringConexion) Dim Adaptador6 As New SqlDataAdapter("SELECT * FROM suppliers", StringConexion) Dim Adaptador7 As New SqlDataAdapter("SELECT * FROM employees", StringConexion) Dim Adaptador8 As New SqlDataAdapter("SELECT * FROM shippers", StringConexion) Dim Adaptador9 As New SqlDataAdapter("SELECT * FROM region", StringConexion) Dim Adaptador10 As New SqlDataAdapter("SELECT * FROM Customers", StringConexion) Dim Adaptador11 As New SqlDataAdapter("SELECT * FROM orders", StringConexion) Dim Adaptador12 As New SqlDataAdapter("SELECT * FROM [Order Details]", StringConexion) Dim Adaptador13 As New SqlDataAdapter("SELECT * FROM Categories", StringConexion) 'Ahora instanciamos una DataSet. MiDataset = New DataSet 'enlazando al dataset con el adapter correspondiente. Adaptador1.Fill(miDataset, "Products") Adaptador2.Fill(MiDataset, "customercustomerdemo") Adaptador3.Fill(miDataset, "customerdemographics") Adaptador4.Fill(miDataset, "[employeeterritories]") Adaptador5.Fill(miDataset, "territories") Adaptador6.Fill(miDataset, "suppliers") Adaptador7.Fill(miDataset, "employees") Adaptador8.Fill(miDataset, "shippers") Adaptador9.Fill(miDataset, "region") Adaptador10.Fill(miDataset, "Customers") Adaptador11.Fill(miDataset, "orders") Adaptador12.Fill(miDataset, "[Order Details]") Adaptador13.Fill(miDataset, "Categories") For i = 0 To miDataset.Tables.Count - 1 CboLista.Items.Add(MiDataset.Tables(i).TableName)
Next 'Seleccionamos por defecto la tabla [employeeterritories]. CboLista.SelectedIndex = 0 'Ahora establecemos el texto correspondiente al título 'de la ventana de nuestro DataGrid1 como "[employeeterritories]". DataGrid1.CaptionText = CboLista.Text For i = 0 To (Me.miDataset.Tables(CboLista.Text).Columns.Count - 1) 'Procedemos a llenar nuestro ListBox1 con los nombres de la 'tabla [employeeterritories], los cuales serán usados para 'elegir en que tabla realizaremos la respectivas búsquedas. Me.ListBox1.Items.Add(Me.miDataset.Tables(CboLista.Text).Columns.I tem(i).Caption) Next MostrarColumnas(CboLista.Text) End Sub 'Esta función servirá para mostrar todas las columnas del DataSet 'que fueron checkeadas en LstColumnas. Private Function FiltrarDataSet() As DataSet Try Dim TemporalDataSet As New DataSet Dim Columna As DataColumn TemporalDataSet = Me.MiDataset.Copy() 'Filtraremos todos los Chek que no están selecionados, 'para después eliminarmos. For i = 0 To Me.Lstcolumnas.Items.Count - 1 If Not Me.Lstcolumnas.GetItemChecked(i) Then ' Recuperamos el nombre para determinar la columna a eliminar. Columna = TemporalDataSet.Tables(CboLista.Text).Columns(Me.Lstcolumnas. _ GetItemText(Me.Lstcolumnas.Items.Item(i))) 'Ahora borramos la columna. TemporalDataSet.Tables(CboLista.Text).Columns.Remove(C olumna) End If Next CboLista.Enabled = True Return TemporalDataSet Catch ex As Exception MsgBox(ex.Message) End Try End Function Private Sub Timer1_Tick(ByVal sender As System.Object, ByVal e As System.EventArgs) _ Handles Timer1.Tick Label1.Visible = False Timer2.Enabled = True Timer1.Enabled = False End Sub Private Sub Timer2_Tick(ByVal sender As System.Object, ByVal e As System.EventArgs) _ Handles Timer2.Tick Label1.Visible = True Timer1.Enabled = True Timer2.Enabled = False End Sub Private Sub LinkLabel1_LinkClicked(ByVal sender As System.Object, ByVal e As _ System.Windows.Forms.LinkLabelLinkClickedEventArgs) Handles LinkLabel1.LinkClicked Dim NewProcess As New Process 'Este es el código Html que nos permite llamar al Outlook Express. NewProcess.Start("mailto:
[email protected]?subject=(ni nguno)") End Sub
Private Sub BtnSalir_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) _ Handles BtnSalir.Click Dim respuesta As DialogResult respuesta = MsgBox(" ¿Seguro que desea salir?", MsgBoxStyle.DefaultButton3 + _ MsgBoxStyle.YesNo + MsgBoxStyle.Question, "Cerrar Aplicación") If respuesta = DialogResult.Yes Then Me.Close() End If End Sub Private Sub BtnTodos_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) _ Handles BtnTodos.Click 'Recordemos que la DataGrid para llenar los resultados es Datagrid2. Me.DataGrid2.DataSource = Me.MiDataset.Tables(CboLista.Text) Dim j As Integer = 0 Try 'Búscamos en cada fila de acuerdo a la columna especificada en el Listbox1. For i = 0 To (Me.MiDataset.Tables(CboLista.Text).Rows.Count 1) If Not (Me.TxtConsulta.Text.Equals(Me.MiDataset.Tables(CboLista.Text).Rows. _ Item(i).Item(ListBox1.SelectedIndex))) Then 'Los registros no encontrados serán eliminados. De esta manera se irá 'filtrando los registros encontrados. Me.MiDataset.Tables(CboLista.Text).Rows.Item(i).Delete() j = j + 1 End If Next Me.DataGrid2.Visible = True Catch ex As IndexOutOfRangeException MsgBox(ex.Message & "Seleccione una opción de búsqueda por campo", MsgBoxStyle.Critical, _ "Aviso Al Usuario") Me.DataGrid2.Visible = False End Try Dim MiDataSetTemporal As New DataSet 'Después de filtrar los registros encontrados, haremos 'una copia de esta tabla en MiDataSetTemporal. MiDataSetTemporal.Tables.Add(Me.MiDataset.Tables(CboLista.Text).Copy) 'Para no modificar el contenido original de MiDataSet, 'cancelaremos los cambios efectuados más arriba. Me.MiDataset.Tables(CboLista.Text).RejectChanges() 'Visualizamos los resultados, vaciando los registros 'de MiDataSetTemporal a la DataGrid2. Me.DataGrid2.DataSource = MiDataSetTemporal.Tables(CboLista.Text) 'Contabilizamos los registros encontrados. Me.DataGrid2.CaptionText = MiDataSetTemporal.Tables(CboLista.Text).Rows.Count - _ j & " registros encontados." 'Cerramos o eliminamos MiDataSetTemporal. MiDataSetTemporal.Dispose() End Sub Public Sub MostrarColumnas(ByVal NombreTabla As String) For i = 0 To (MiDataset.Tables(NombreTabla).Columns.Count - 1) Me.ListBox1.Items.Add(MiDataset.Tables(NombreTabla).Columns.Item(i ).Caption) Next End Sub Private Sub ChkTodos_CheckedChanged(ByVal sender As System.Object, ByVal e As System.EventArgs) _ Handles ChkTodos.CheckedChanged
If ChkTodos.Checked = True Then 'Marcamos todos las casillas For i = 0 To Lstcolumnas.Items.Count - 1 Lstcolumnas.SetItemChecked(i, True) Next Else 'Desmarcamos todos las casillas For i = 0 To Lstcolumnas.Items.Count - 1 Lstcolumnas.SetItemChecked(i, False) Next End If End Sub Private Sub TxtConsulta_TextChanged(ByVal sender As System.Object, ByVal e As System.EventArgs) _ Handles TxtConsulta.TextChanged BtnTodos.Enabled = True End Sub Private Sub BtnMostrarColumnas_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) _ Handles BtnMostrarColumnas.Click Dim NumeroColumnas As Integer Dim NombreColumnas As String GroupBox1.Enabled = True ChkTodos.Enabled = True NumeroColumnas = MiDataset.Tables(CboLista.Text).Columns.Count Lstcolumnas.Items.Clear() For i = 0 To (NumeroColumnas - 1) 'Llenamos LstColumnas con todas los campos de la tabla seleccionada en CboLista. NombreColumnas = MiDataset.Tables(CboLista.Text).Columns.Item(i).Caption Lstcolumnas.Items.Add(NombreColumnas) Next DataGrid1.CaptionText = CboLista.Text BtnMostrarColumnas.Enabled = False BtnFiltrarColumnas.Enabled = True CboLista.Enabled = False ListBox1.Items.Clear() 'Llamamos al procedimiento para llenar el ListBox1. MostrarColumnas(CboLista.Text) End Sub Private Sub BtnFiltrarColumnas_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) _ Handles BtnFiltrarColumnas.Click BtnMostrarColumnas.Enabled = True BtnFiltrarColumnas.Enabled = False Dim opcion As String = CboLista.SelectedItem 'Llenamos la DataGrid1 con los datos filtrados. DataGrid1.DataSource = Me.FiltrarDataSet() DataGrid1.DataMember = CboLista.Text End Sub End Class Aquí les muestro la interface de la aplicación, donde se puede observar todo los resultados.