Marche un Proveedor de Datos Servidor Cliente !!

El Proveedor de Datos Servidor Cliente esta terminado y se llama ProveedorDatosServidor. ¿ Cómo se usa?… véanlo aquí y no se olviden de bajarlo!!.

Después de romperme la cabeza intentando que esta idea funcione; después de hacer el control y deshacerlo varias veces; después de probarlo y re-probarlo… aquí está.

Cumple con todos los deseos que había pedido en mi primer post  Proveedor de Datos Servidor Cliente y ,sin quererlo, con algunos mas.

Lo he probado con tablas de un millón y medio de registros, y lo que se percibe es que los datos están en el cliente. He buscado un valor en particular; he ido al último registro; al primero; saltado 300 registro para adelante y 150 para atrás… y para todo tuve el mismo tiempo de respuesta. Y modestia aparte este tiempo es buenísimo.

La utilización del lado del servidor es muy simple, y si se tienen reglas de negocio bien diseñadas, la actualización de la tabla en SQL es muy fácil.

La programación del lado del cliente es sencilla si se usa de forma sincrónica, y solo se complica un poco si se usa de forma asincrónica  por el uso eventos (ya saben lo que cuesta pensar un programa cuando no es secuencial).

A la idea original solo le agregué un directiva para que se guarde el indice en una variable de aplicación en el IIS para, que de esta manera, el paginado en SQL (que es lo que mas tarda) se haga una sola vez. Teniendo en cuenta que para tablas muy grandes este indice es casi estático y no va a ser necesario consultarlo cada vez.

Pero basta de charla y vamos a un ejemplo:

Yo use estas reglas para acceder a los datos en SQL Server. Ustedes pueden hacerlas de la manera que prefieran.

Public Class ReglaAfiliados
Public Shared Function ConsultaTodo(ByRef Desde As Object, ByVal Hasta As Object) As SqlDataAdapter
   If Desde Is Nothing Then Desde = ""
   If Hasta Is Nothing Then Hasta = "ZZZZ"
   Dim Adaptador = New System.Data.SqlClient.SqlDataAdapter
   Dim Cadena = "Data Source=MiServidor;Initial Catalog=MiDataBase;Persist Security Info=True;User ID=MiId;Password=MiPassword"
   Dim Conexion As System.Data.SqlClient.SqlConnection = New System.Data.SqlClient.SqlConnection(Cadena)
   Adaptador.SelectCommand = New System.Data.SqlClient.SqlCommand()
   Adaptador.SelectCommand.Connection = Conexion
   Adaptador.SelectCommand.CommandText = "Select ApeNom,Documento from Afiliados where and ApeNom >=@Desde and ApeNom<@Hasta"
   Adaptador.SelectCommand.Parameters.Add(New SqlParameter("@Desde", Desde))
   Adaptador.SelectCommand.Parameters.Add(New SqlParameter("@Hasta", Hasta))
   Return Adaptador
End Function
Public Shared Function ConsultaTodoIndice() As SqlDataAdapter
    Dim Cadena = "Data Source=MiServidor;Initial Catalog=MiDataBase;Persist Security Info=True;User ID=MiId;Password=MiPassword"
    Dim Adaptador = New System.Data.SqlClient.SqlDataAdapter
    Dim Conexion As System.Data.SqlClient.SqlConnection = New System.Data.SqlClient.SqlConnection(Cadena)
    Adaptador.SelectCommand = New System.Data.SqlClient.SqlCommand("Select * from (Select ROW_NUMBER() OVER (ORDER BY ApeNom) -1 AS FilaNumero,ApeNom from Afiliados a)  as Indice where (cast(FilaNumero as decimal)/500) - (FilaNumero/500) = 0", Conexion)
    Return Adaptador
End Function

En principio vemos dos funciones: La primera, ConsultaTodo, sirve para hacernos de los datos en si.
La segunda, ConsultaTodoIndice, es la que crea el índice.
Para consultar los datos del servidor debemos hacer un Select sobre la tabla con las siguientes condiciones:
1. Debe tener un Order By sobre un campo que a su vez posea indice.
2. Debe incluir en su clausula Where dos parámetros que acoten el resultado por el campo ordenado (Campo mayor o igual @Desde y Campo menor @Hasta).
3. El parámetros @Desde debe ser por defecto el menor valor posible del campo ordenado.
4. El parámetro @Hasta debe ser por defecto el mayor valor posible del campo ordenado.
En este caso yo use la programación directa del SqlDataAdapter, pero podría usarse un StoreProcedure como el siguiente en la propiedad CommandText de nuestro adaptador.

 CREATE PROCEDURE [dbo].[afiliados_ConsultaTodo] 	@Desde	varchar(201) = '', 	@Hasta	varchar(201) = 'ZZZZ' AS SET NOCOUNT ON Select ApeNom,Documento from Afiliados where ApeNom >= @Desde and ApeNom < @Hasta Order by ApeNom

Lo que dejaria nuestra función de la siguiente manera:


 Public Shared Function ConsultaTodo(ByRef Desde As Object, ByVal Hasta As Object) As SqlDataAdapter
 Dim Adaptador = New System.Data.SqlClient.SqlDataAdapter
 Dim Cadena = "Data Source=MiServidor;Initial Catalog=MiDataBase;Persist Security Info=True;User ID=MiId;Password=MiPassword"
 Dim Conexion As System.Data.SqlClient.SqlConnection = New System.Data.SqlClient.SqlConnection(Cadena)
 Adaptador.SelectCommand = New System.Data.SqlClient.SqlCommand()
 Adaptador.SelectCommand.Connection = Conexion
 Adaptador.SelectCommand.CommandType =Data.CommandType.StoredProcedure
 Adaptador.SelectCommand.CommandText = "afiliados_ConsultaTodo"
 Adaptador.SelectCommand.Parameters.Add(New SqlParameter("@Desde", Desde))
 Adaptador.SelectCommand.Parameters.Add(New SqlParameter("@Hasta", Hasta))
 Return Adaptador
 End Function

La segunda función sera usada por el control para armar el indice. Analicemos este Select:

Select * from (Select ROW_NUMBER() OVER (ORDER BY ApeNom) -1 AS FilaNumero,ApeNom from Afiliados)  as Indice where (cast(FilaNumero as decimal)/500) - (FilaNumero/500) = 0

A grandes rasgos lo que hacemos es numerar la tabla ordenada por ApeNom y luego quedarnos con los registros múltiplos de 500.

Entonces: La clausula Order By debe ser la misma que la de la primera función. Y el número 500 nos permitirá variar el tamaño de la página, ya que cada uno de los registros obtenidos marcará el comienzo de cada página.
Mas adelante veremos como jugar con este número para obtener el mejor rendimiento en las consultas.

Una vez creada esta regla podemos proceder a usar el control en nuestra Página.

Lo primero que haremos será incluir en nuestro proyecto Web una referencia a la librería MoniMisi2.
Luego crearemos una pagina web que incluirá el SriptManager y un ProveedorDatosServidor de la misma librería.
Ahora procedemos a la configuración de nuestro control. He aquí un ejemplo:

<%@ Page Language="VB" AutoEventWireup="false" CodeFile="Afiliados.aspx.vb" Inherits="Afiliados" %>

<%@ Register Assembly="MoniMisi2" Namespace="MoniMisi2" TagPrefix="MM2" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
    <title></title>
</head>
<body>
    <form id="form1" runat="server">
    <MM2:ScriptManager ID="ScriptManager" runat="server">
    </MM2:ScriptManager>
    <MM2:ProveedorDatosServidor ID="Afiliados" runat="server" WebMethod="ManejoAfiliados" Encontrado="MostrarNombre" FinDeArchivo ="Fin" PrincipioDeArchivo ="Principio">
        <Ordenes>
            <MM2:OrdenDatos Columna="ApeNom"></MM2:OrdenDatos>
        </Ordenes>
    </MM2:ProveedorDatosServidor>
    <asp:Button ID="Primero" runat="server" Text="Primer Registro" OnClientClick="$find('Afiliados').Arribar();return false"></asp:Button>
    <asp:Button ID="Avanzar" runat="server" Text="Avanzar" OnClientClick="$find('Afiliados').Avanzar();return false"></asp:Button>
    <asp:Button ID="Avanzar10" runat="server" Text="Avanzar 10" OnClientClick="$find('Afiliados').Avanzar({Salto:10});return false"></asp:Button>
    <asp:Button ID="Retroceder5" runat="server" Text="Retroceder 5" OnClientClick="$find('Afiliados').Retroceder({Salto:5});return false"></asp:Button>
    <asp:Button ID="Ultimo" runat="server" Text="Ultimo Registro" OnClientClick="$find('Afiliados').Fondear();return false"></asp:Button>
    </form>

    <script type="text/javascript">
        function MostrarNombre(sender, e) {alert(e.ApeNom);}
        function Fin(sender, e) { alert("Fin de Archivo")}
        function Principio(sender, e) { alert("Principio de Archivo")}
    </script>

</body>
</html>

Vemos que el ProveedorDatosServidor posee alguna propiedades que paso a describir:

WebMethod: Nombre del WebMethod que se invocara en el servidor para tener acceso a los datos (veremos como programarlo mas adelante).
Encontrado: Nombre de la función JavaScript que se ejecutará cada vez que se mueve el puntero del control.
FinDeArchivo: Nombre de la función JavaScript que se ejecutará cada vez que el puntero del control supere el final de la tabla.
PrincipioDeArchivo: Nombre de la función JavaScript que se ejecutará cada vez que el puntero del control supere el principio de la tabla.

En este ejemplo he incluido algunos botones que permiten mover el puntero de nuestro control para comprobar el funcionamiento.
No hay mucho que explicar: simplemente el click del botón ejecuta una función que busca la referencia de nuestro ProveedorDatosServidor cuyo id es Afiliados y luego invocamos al método que representa la acción que queremos ejecutar.
Finalizada esta acción se dispara el evento Encontrado que llamará a nuestro función MostrarNombre, la cual muestra un cartel con el campo ApeNom de la tabla Afiliados.
Si se llega al principio o fin de la tabla se disparan los eventos FinDeArchivo o PrincipioDeArchivo respectivamente lo que llamará a nuestras funciones Principio y Fin para mostrar un cartel de aviso.
Fácil ¿No?

Acciones que se pueden invocar:

De movimiento : Sirven para recorrer la tabla o consultar un registro en particular.

Arribar({Orden:entero,Pagina:entero}) Pone el puntero en el principio de la Pagina/Tabla
Orden: Por defecto es cero. Si no se especifica se mantiene el actual.
Pagina: Si no se especifica es la primera pagina.

Fondear({Orden:entero,Pagina:entero}) Pone el puntero al final de la Pagina/Tabla
Orden: Por defecto es cero. Si no se especifica se mantiene el actual.
Pagina: Si no se especifica es la última pagina.

Avanzar({Salto:entero}) Posiciona el puntero en el N° registro siguiente.
Salto: Si no se especifica es 1.

Retroceder({Salto:entero}) Posiciona el puntero en el  N° registro anterior.
Salto: Si no se especifica es 1.

Buscar({Orden:entero,Valor:object}) busca el registro cuyo contenido coincida con el parámetro Valor o el primero mayor, según el orden establecido.
Orden: Por defecto es cero. Si no se especifica se mantiene el actual.
Valor: Valor que se quiere buscar. Debe ser del mismo tipo que el campo por el cual esta ordenado.

De Actualización: Hacen una llamada al servidor con los datos que se deben actualiza para que este los refleje en la tabla SQL. Eventualmente actualizan la posición del puntero en el cliente.

Agregar({RegistroActual:object}) Agrega un nuevo registro
RegistroActual: Un registro nuevo con los datos que se quiere agregar.

Modificar({RegistroAnterior:object,RegistroActual:object}) Modifica el registro señalado por el puntero.
RegistroAnterior: Si no se especifica es el registro señalado por el puntero.
RegistroActual: Un registro nuevo con los datos que se quiere actualizar.

Eliminar({RegistroAnterior:object}) Elimina el registro señalado por el puntero
RegistroAnterior: Si no se especifica es el registro señalado por el puntero.

Actualizar({Registros:array}) Actualiza un grupo de registros.
Registros: Es un array con todos los registro que se desea Agregar/Modificar/Eliminar.

Otros:

Clonar() Devuelve una copia idéntica del registro actual.
Limpiar() Borra todo los registros del lado del cliente.
IrA(Puntero) mueve el puntero a una posición memorizada previamente.

Propiedades :

Puntero : coordenada indice/pagina/registro en la cual se encuentra posicionado el control.
Registro: conjunto de campos a los cuales apunta la propiedad Puntero.

Eventos:

Encontrado: Cada vez que se mueve el puntero si no es principio ni fin de archivo.
FinDeArchivo: Cada vez que el puntero supere el final de la Tabla.
PrincipioDeArchivo: Cada vez que el puntero supere el principio de la Tabla.
Agregado: una vez que se agregó el registro en el servidor.
Modificado: una vez que se modificó el registro en el servidor.
Eliminado: una vez que se eliminó el registro en el servidor.
Actualizado: una vez que se actualizaron los registros en el servidor.

Por último y para que todo funcione programaremos el WebMethod que en nuestro caso se debería llamar ManejoAfiliados.

Imports System.Web.Services
Imports MoniMisi2

Partial Class Afiliados
  Inherits System.Web.UI.Page

  <WebMethod()> _
  Public Shared Function ManejoAfiliados(ByVal pContrato As ProveedorDatosServidor.Contrato) As ProveedorDatosServidor.Contrato
    Dim Contrato As ProveedorDatosServidor.Contrato = pContrato
    If Contrato.Operacion.Nombre = "Leer" Then
      Contrato = ProveedorDatosServidor.LeerFilasPaginadas(Contrato, ReglaAfiliado.ConsultaTodo(Nothing, Nothing), ReglaAfiliado.ConsultaTodoIndice, "IndiceApeNomAfiliados")
    End If
    If Contrato.Operacion.Nombre = "Modificar" Then
      ''Modificar el registro 
    End If

    If Contrato.Operacion.Nombre = "Eliminar" Then
      ''Eliminar el registro 
    End If

    If Contrato.Operacion.Nombre = "Agregar" Then
      ''Agregar el registro 
    End If

    If Contrato.Operacion.Nombre = "Actualizar" Then
      ''Actualizar el registros 
    End If

    Return Contrato

  End Function
End Class

Lo mas importante en este código es la forma de invocar la lectura de los datos.
Esta se realiza a través del método ProveedorDatosServidor.LeerFilasPaginadas.
Lleva como parámetros el contrato; el adaptador para acceder a la tabla; el adaptador para acceder al indice y un id para poder guardar en variable de aplicación el indice una vez obtenido.
Luego consultando por Contrato.Operacion.Nombre podemos saber la acción a ejecutar para reflejar los cambios en la tabla SQL.

En post subsiguientes iré contando algunos detalles más acerca del uso de este control. Por ahora les dejo esto para que se vayan entreteniendo.

Bajen sin compromiso…

MoniMisi2.dll

Anuncios

6 Respuestas a “Marche un Proveedor de Datos Servidor Cliente !!

  1. Pingback: Una Grilla para mi Proveedor de Datos Servidor Cliente | MoniMisi

  2. Pingback: Nueva Grilla para ASP.net sin uso de Postback | MoniMisi

  3. Pingback: Nuevo Formulario para ASP.net | MoniMisi

  4. Pingback: Nuevo control de ingreso de fechas con calendario. | MoniMisi

  5. Pingback: Nuevo Menu dinámico enlazado a tabla SQL para ASP.net. | MoniMisi

  6. Pingback: Ejemplo de Control de Usuario con dos Combos relacionados | MoniMisi

Responder

Introduce tus datos o haz clic en un icono para iniciar sesión:

Logo de WordPress.com

Estás comentando usando tu cuenta de WordPress.com. Cerrar sesión / Cambiar )

Imagen de Twitter

Estás comentando usando tu cuenta de Twitter. Cerrar sesión / Cambiar )

Foto de Facebook

Estás comentando usando tu cuenta de Facebook. Cerrar sesión / Cambiar )

Google+ photo

Estás comentando usando tu cuenta de Google+. Cerrar sesión / Cambiar )

Conectando a %s