miércoles, 8 de octubre de 2008

Ordenar, paginar y buscar en un GridView con Datasource

En mi entrada anterior estaba metido en una página Web sencilla en donde se visualiza un GridView con capacidades de paginación, ordenación y búsqueda.

Como buen desarrollador me lié a escribir código y clases para controlar que cuando paginara no me desordenara, y que cuando buscara me mantuviera la última posición de la página y el orden adecuado.

Y cuando llevaba tropecientas mil líneas y una entrada en este mi blog, leí en una página de MSDN que Microsoft aconsejaba hacer estas operaciones con una conexión a un Datasource.

Le eché una ojeada y me lleve la desagradable sorpresa que me ponía todo el código en la página aspx.

Dándole vueltas, recordé que todo lo que hay en la pantalla lo puedo gestionar desde el “code behind” y dejé solamente la invocación al control en el aspx:
<asp:sqldatasource id="SqlDataSource" runat="server">
El siguiente contratiempo es que yo intento mantener una clara estructura de tres capas (a saber, presentación, negocio y dato), aunque sea en un proyecto enano como este. Por lo cual empecé por detrás y me fui a la capa de datos en donde comprobaba el parámetro de cadenaDeBusqueda (string) para saber si tenía que utilizar una cadena de SQL u otra.

Public Class accesoDatos

Public Function cadenaComandoSelect(ByVal cadenaBusqueda As String) As String
Dim sql As New StringBuilder

' Bandera que indica si estoy entrando en el bucle For Each por primera vez.
Dim primeraPagina As Boolean = True

sql.Append("SELECT ")
sql.Append("* ")
sql.Append("FROM Tabla ")

' Si hay parámetros de búsqueda...
If cadenaBusqueda <> String.Empty Then

' Lo despieza por palabras usando el espacio como separador.
Dim palabrasBusqueda() As String = cadenaBusqueda.Split(" ")

' Y por cada palabra hace una búsqueda en todos los campos
For Each palabra As String In palabrasBusqueda
'Si es la primera vez que entra en el bucle añade el Where y pone a false la bandera
If primeraPagina = True Then
sql.Append("WHERE ")
primeraPagina = False

Else
' Si estamos entrando en otro bucle, añade el AND' para hacer restrictivas las búsquedas.
sql.Append(" AND ")
End If

sql.Append("(campoTablaSQL LIKE '%" & palabra & "%' OR ")
sql.Append("campoTablaSQL1 LIKE '%" & palabra & "%')")

Next
End If

Return sql.ToString

End Function
End Class


Cómo os habréis dado cuenta, realmente no conecto aquí con la base de datos, pero en mi opinión prefiero tener todas las cadenas SQL juntas en esta capa. Me es más sencillo para el mantenimiento y evolución de la aplicación.

Después me voy a la capa de negocio, en donde tengo las reglas del idem que trata los datos que van y vienen de datos y presentación, y creo un método que envía la cadena de búsqueda y devuelve la cadena SQL de SELECT apropiada.

Public Class Articulo
Public cadenaBusqueda As String
Dim datos As New accesoDatos

Public Function comandoSelect() As String
Return datos.cadenaComandoSelect(cadenaBusqueda)
End Function

End Class

Por último me voy a la capa de código de la capa de presentación y realizo la conexión de GridView con el SqlDatasource.

Dim articulo As New Articulo

Public Sub inicializaAdaptadorSQL()

articulo.cadenaBusqueda = txtbBuscar.Text

SqlDataSource.ConnectionString = ConfigurationManager.ConnectionStrings("conexionStringSQL").ToString
SqlDataSource.SelectCommand = articulo.comandoSelect

gvArticulos.DataSourceID = "SqlDataSource"

End Sub


Y con esto cumplo dos paradigmas en mi forma de desarrollar. Primero, intentar picar el menos código posible. Y conseguir el que el mantenimiento futuro sea el más sencillo.

Es una ruptura del modelo de las capas porque realmente realizo la llamada a los datos desde la capa de presentación, y lo de POO me los salte a la torera, pero cuando comparo las 5 horas que llevaba contra los 15 minutos en que termine la conexión y el GridView adquirió las actuaciones que necesitaba… pues como que no hay color.

Perdonar si he publicado un campo de la clase como público sin pasarlo por una propiedad, que sería lo suyo, pero así reduzco el texto del código de ejemplo.

1 comentario:

Anónimo dijo...

oie muy bueno tu articulo aki lo poesteo traducido a c#

public class accesoDatos
{

public string cadenaComandoSelect(string cadenaBusqueda)
{
//para poder habilitar el stringbuidel necesitas usar el using System.Text
StringBuilder sql = new StringBuilder();
// Bandera que indica si estoy entrando en el bucle For Each por primera vez.
bool primeraPagina = true;
sql.Append("SELECT ");
sql.Append("* ");
sql.Append("FROM Drivers ");

// Si hay parámetros de búsqueda...
if (cadenaBusqueda != string.Empty)
{
// Lo despieza por palabras usando el espacio como separador.
string[] stringSeparadores = new string[] {" "};
string[] palabrasBusqueda = cadenaBusqueda.Split(stringSeparadores, StringSplitOptions.None);

// Y por cada palabra hace una búsqueda en todos los campos
foreach (string palabra in palabrasBusqueda)
{
//Si es la primera vez que entra en el bucle añade el Where y pone a false la bandera
if (primeraPagina == true)
{
sql.Append("WHERE ");
primeraPagina = false;
}

else
{
// Si estamos entrando en otro bucle, añade el AND' para hacer restrictivas las búsquedas.
sql.Append(" AND ");
}

sql.Append("(SO LIKE '%" + palabra + "%' OR ");
sql.Append("Compañia LIKE '%" + palabra + "%')");
}

}

return sql.ToString();

}
}



--------------------------------------------------------------

public class Articulo
{
public string cadenaBusqueda;
accesoDatos datos = new accesoDatos();

public string comandoSelect()
{
return datos.cadenaComandoSelect(cadenaBusqueda);
}
}

-------------------------------------------------------

protected void btnBuscar_Click(object sender, EventArgs e)
{
Articulo Buscar = new Articulo();
Buscar.cadenaBusqueda = this.txtBuscar.Text;
this.txtBuscar.Text = "";
SqlDataSource Source = new SqlDataSource();
Source.ConnectionString = ConfigurationManager.ConnectionStrings["CadenaConexion"].ToString();
Source.SelectCommand = Buscar.comandoSelect();
GridView1.DataSource = "Source";
GridView1.DataBind();
}