Esta es una traducción aproximada del artículo Business Apps Example for Silverlight 3 RTM and .NET RIA Services July Update: Part 2: Rich Data Query en la serie “Business Apps Example for Silverlight 3RTM and .NET RIA Services” publicada en inglés por Brad Abrams
en 2009. Los artículos originales están basados en su charla de Mix09 “Building Amazing Business Centric Applications with Microsoft Silverlight 3”.

 

Consultas densas de datos

Como prácticamente todo el software empresarial se dedica a gestionar información, tiene sentido ahora considerar el papel de los servicios RIA de .NET en tal campo.

Uno de los aspectos principales de aplicaciones RIA es la transferencia de información del servidor al cliente y viceversa. Para demostrar los servicios RIA de .NET necesitamos entonces una fuente de información. En su ejemplo original Brad usa la base de datos Northwind con la variante de una nueva tabla añadida.

 


Este ejmplousa la base de datos Northwind.

 

En esta versión yo utilicé la misma base de datos, pero traduje el nombre de la tabla y las columnas. La base de datos con la tabla modificada está incluida en la solución resultante del artículo anterior.

 

Similar al artículo de Brad Adams, se usa una tabla para Súper Empleados.

 

Para comenzar, vamos a utilizar Entity Framework y crear un modelo de la tabla anterior. En el proyecto MiAplicacion.Web, hago clic con el botón derecho en Models, y selecciono Agregar->Nuevo Elemento… y escogiendo ADO.NET Entity Data Model. (Más detalles de cómo usar Entity Framework se pueden ve en el procedimiento para generar un modelo conceptual a partir de una base de datos.)

 


El elemento ADO.NET Entity Data Model traza el mapa objeto-relacional para nuesta entidad.

 

Como se puede ver, la solución creada por Visual Studio tiene carpetas y archivos con nombres en inglés. De igual manera, los nombre de ciertos elementos, como por
ejemplo el que acabamos de usar, también están en inglés. Supongo que en la versión final, la plantilla generará nombres en español.

 


Modelo de la tabla SuperEmpleado

 

Teniendo un modelo en servidor, la pregunta es ¿cómo puede el cliente de Silverlight acceder los datos? Muchos programas empresariales usan al principio una sencilla
arquitectura de dos etapas:

 


Sistemas una etapa son simples pero pueden resultar rígidos y difíciles de mantener.

 

Aunque es simple y fácil de usar y entender, tal estructura termina siendo rígida y difícil de expandir. Y no es muy aplicable en este caso pues, a fin de cuentas, tenemos de por medio una conexión de red. Para resolver el problema, es necesario añadir más niveles o etapas de separación siendo aquí donde los servicios RIA demuestran su utilidad al simplificar la labor de crear sistemas flexibles y expandibles basados en WCF y los servicios de información de ADO.NET. En el diagrama siguiente, tales componentes se encuentran cerca de las orillas en la zona central llamada código con reglas empresariales.

 


Sistemas con múltiples etapas ofrece mayor flexibilidad y expansibilidad.

 

Los servicios RIA trabajan como un intermediario entre la interfaz al usuario y el acceso a la información. Tal mediación abre nuevas vías hacia diferentes fuentes de datos. como por ejemplo una base de datos tradicional, un conjunto de objetos simples de .NET (POCO), información en la nube (por ejemplo, Azure), mediante servicios de Web y otros más. Para crear un nuevo servicio, haga clic con el botón derecho en el proyecto del servidor, MiAplicacion.Web, y seleccione Domain ServiceClass. Un posible nombre para la nueva clase es SuperEmpleadoService. Si bien el nombre suena extraño, fue escogido a propósito por razones que serán evidentes dentro de poco.

 


Creación del nuevo servicio de datos SuperEmpleadoService.

 

Al aceptar la creación del servicio se inicia un asistente que permite escoger la fuente de datos. El asistente busca posibles fuentes en el proyecto y sugiere el existente modelo Northwind.edmx, pero uno puede escoger cualquier otro disponible, como un a clase de Linq2Sql, o una clase simple basada en el CLR. Nótense las opciones elegidas para el servicio.

 


Opciones disponibles al crear un servicio de datos.

 

El servicio generado contiene funciones esqueleto para las operaciones básicas de consulta, actualización, inserción y borrado de datos que de seguro necesitarán ser modificadas para el contexto del sistema. Sin embargo, al principio sólo utilizaremos la función GetSuperEmpleadoConjunto (de nuevo, los nombres podrían ser mejores, pero prefiero concentrarme en el proceso por el momento), que está modificada como sigue:

 

public IQueryable GetSuperEmpleadoConjunto()
{
    return this.ObjectContext.SuperEmpleadoConjunto
                .Where(emp => emp.Numeros > 100)
                .OrderBy(emp => emp.EmpleadoId);
}

 

Luego de hacer las modificaciones, hay que regenerar la aplicación para que el proyecto cliente obtenga acceso al nuevo servicio. El vínculo entre ambos proyectos fue establecido por la plantilla Silverlight Business Application al crear la solución y puede verse en las propiedades del proyecto cliente.


El enlace entre el servidor y el cliente se activa en la configuración del cliente.

Yendo ahora al cliente, en la carpeta Views, abra la página Home.xaml. Luego, desde el cuadro de herramientas, arrastre y suelte un control DataGrid en el código de la página justo antes del cierre del StackPanel. Este sistema de arrastre sirve para cualquier control del cuadro. Una vez depositado el control, añada estas modificaciones:

 

 

En el código subyacente, se debe añadir una referencia al servicio creado en el servidor. Un beneficio del enlace entre proyectos es que el ámbito del servicio está disponible “automágicamente” en cliente y visible mediante IntelliSense.

 

Namespace

 

var contexto = new SuperEmpleadoContext();
dataGrid1.ItemsSource = contexto.SuperEmpleados;
contexto.Load(contexto.GetSuperEmpleadoConjuntoQuery());

 

En la línea 1 se crea el contexto de datos para SuperEmpleados. El nombre SuperEmpleadoContext es generado de manera automática basado en la convención de que una clase del dominio que termine Service en el servidor, recibirá el sufijo Context en el cliente. Según mi experiencia, tal convención se limita a los sufijos en inglés–Service y Context, lo que explica mi escogencia del nombre SuperEmpleadoService. La línea 2 establece el enlace entre entre la cuadrícula dataGrid1 y SuperEmpleados. Finamente, la tercera línea descarga la información del servidor en el control mediante el método de consulta GetSuperEmpleadoConjuntoQuery, que a su vez invoca el método GetSuperEmpleadoConjunto en el servidor. Es importante hacer notar también que toda ésta actividad se produce de manera asíncrona sin interferir con la agilidad de respuesta de la interfaz al usuario. Aun así no hubo necesidad de envolverse en las complicaciones inherentes a tales comunicaciones.

 


Super Empleados presentados en la cuadrícula dataGrid1.

 

La imagen anterior muestra el resultado de nuestro trabajo. Lo bueno es que funciona como esperábamos; lo malo es que se trajo todos los registros de la tabla de SuperEmpleados. Por lo general, en software de Web, es mejor usar paginación para evitar largas pausas mientras se obtienen los datos. Además es conveniente ofrecer opciones como filtraje y ordenación. Veamos qué está envuelto en proveer tales funciones. Empecemos por borrar el código subyacente añadido a Home.xaml.cs, puesto que va a ser reemplazado con un control que permite hacer la conexión con el servidor en lenguaje declarativo. Para ello, basta arrastrar un control DomainDataSource, soltarlo en Home.xaml, e incluir algunas modificaciones.

 


DnDDataSource

 

Con el fin de poder referirse al contexto SuperEmpleadoContext, inserte la siguiente línea en la declaración del elemento Page:

 

xmlns:Apl="clr-namespace:MiAplicacion.Web.Services"

 

Luego modifique el control DomainDataSource para conectarlo al contexto y al método de consulta:

 


    
        
    

 

En este caso el método a usar se especifica en la tercera línea y el contexto se define en la sexta. Nótese también cómo en la cuarta línea se establece un máximo de 20 artículos a trasferir en cada requisición. Si los empleados vienen ahora en grupos de 20, es necesario poder moverse de un lote al siguiente. Esa es exactamente la función del DataPager que ahora añadimos a la página. Se puede usar el método de arrastre desde el cuadro de herramientas, o simplemente declararlo en el código a mano. Da igual. Una vez colocado debajo de la cuadrícula, se agrupan ambos controles en un StackPanel.

 

Reportando el progreso de la consulta

La plantilla de la aplicación creó originalmente una carpeta llamada Libs en el proyecto cliente. Allí se encuentra un elemento muy útil llamado ActivityControl que despliega una barra de progreso y tiene la capacidad de ser enlazado a otros componentes mediante Bindings. Vamos a usar ese control en esta página para informar al usuario cuando el cliente está esperando la llegada de datos del servidor, por lo que hay que poner una referencia en la declaración del elemento Page:

 

xmlns:act="clr-namespace:System.Windows.Controls;assembly=ActivityControl"

 

El ActivityControl envuelve el StackPanel completo que contiene la cuadrícula para poder así deshabilitar ambos controles. Al final el código queda de esta manera:

 



    
    
    
    

 

En la línea 11 se declara el control de paginado y la 13 lo enlaza a la misma fuente de datos de la cuadrícula. Otro detalles la indicación la línea 11 instruyendo al control a que despliegue 10 empleados a la vez. De igual manera, el ActivityControl está enlazado a la propiedad IsBusy de la fuente de datos, apareciendo en la pantalla cuando es necesario. Este enlace mediante Bindings es genial pues permite relacionar los controles con cualquier tipo de fuentes de datos, como por ejemplo, servicios WCF, servicios REST, y así por el estilo. Es hora de ejecutar el programa de nuevo (F5) y ver qué tal vamos…

 


El cliente pide empleados en grupos de 20 y despliega 10 a la vez.

 

Esta vez, se ven 10 Súper Héroes en la cuadrícula, pero en realidad se han descargado 20 desde el servidor. Es fácil de comprobar si se va a la siguiente página que se despliega de inmediato. Avanzando una página más causa la descarga de otros 20 empleados provocando una pequeña aunque discernible pausa. Pero todavía hay más: al hacer un clic en cualquiera de los encabezados se ordena la tabla entera basada en la columna escogida. Esta operación se ejecuta en el servidor enviando luego los primeros 20 empleados organizados bajo el nuevo criterio. Note cómo no tuvimos que escribir código para tal operación ni en el cliente ni en el servidor. Es parte de las labores de Linq hacer que tales cosas simplemente funcionen como deben. ¿Qué tal ahora si agrupamos los datos?

 

xmlns:datos="clr-namespace:System.Windows.Data;assembly=System.Windows.Controls.Ria"

    

 


Súper Héroes agrupados por casa editorial.

 

También es fácil proveer la facilidad de filtrar la información; por ejemplo, se podría filtrar a los empleados por su origen. Abramos entonces un un espacio dónde colocar el filtro:

 


    
    

 

En mi caso, yo puse el StackPanel justo antes de la cuadrícula. Una vez en su sitio, falta crear el filtro en la fuente de datos:

 


    
        
            
            
        
    

 

Es importante hace notar que en los últimos pasos, desde la agrupación en adelante, el código Xaml es un poco diferente al del artículo original de Brad. Esto es debido a ciertos cambios aparecidos en recientes versiones de Silverlight y los servicios RIA. Al ejecutar la aplicación una vez más aparece el campo para poder filtrar desde el servidor a los empleados recibidos por el cliente.

 


Súper Héroes filtrados por su origen.

 

Todo está muy bien, pero ¿y si quisiéramos más bien que el campo del filtro ofreciera completar nuestro término de filtraje? En ese caso hay que primero obtener todos los posibles valores de la columna Origen en la tabla de SuperEmpleados. El cliente no tiene tal lista, por lo que debe obtenerla del servidor mediante una nueva clase y método de consulta:

 

public class Origen
{
    public Origen()
    { }

    [Key]
    public string Nombre { get; set; }

    public int Cuenta { get; set; }
}

 

public IQueryable GetOrigenes()
{
    var c = (from emp in ObjectContext.SuperEmpleadoConjunto
             select emp.Origen).Distinct()
             .Select(nombre => new Origen
             {
                 Nombre = nombre,
                 Count = ObjectContext.SuperEmpleadoConjunto
                 .Count(emp => emp.Origen.Trim() == nombre.Trim())
             });

    c = c.Where(emp => emp.Nombre != null);
    return c;
}

 

Con esta nueva consulta, es posible entonces remplazar el TextBox del filtro con un control AutoCompleteBox. Aunque el proceso es simple, requiere un poquito de trabajo extra. Para empezar, en este caso es probablemente mas práctico arrastrar y soltar el control en el lugar apropiado del código Xaml. Así dejamos que Visual Studio automáticamente añada las referencias necesarias.

 


 

La PlantillaDatosOrigen provee información sobre la apariencia de la lista de auto-completado del filtro. Siendo un recurso estático, lo podemos poner en Assets\Styles.xaml.

 


    
        
        
            
            
        
    

 

Finalmente, hay que cargar la lista de posibles términos en el control. Para ello usamos un trozo de código subyacente en Home.xaml.cs:

 

var c = dds.DomainContext as SuperEmpleadoContext;
FiltroOrigen.ItemsSource = c.Origens;
c.Load(c.GetOrigenesQuery());

 

En la segunda línea, c.Origens es el nombre generado automáticamente para EntitySet. Desafortunadamente, el generador de código nuevamente se disloca con nombres en español. Bueno, pero ya con todos los cambios requeridos, es hora de probar qué tal funciona con F5.

 

Posibles valores para el campo del filtro son provistos automáticamente.

 

Comprobación de actualizaciones

A pesar de la gran variedad formas en que se puede visualizar la información usando consultas densas de datos, de poco servirían sin la capacidad de hacer modificaciones. Veamos entonces cómo incorporar tal habilidad en el sistema. Con el fin de demostrar el mecanismo de comprobación de actualizaciones Brad hizo cambios algo extensos en Home.xaml por lo que es mejor no más copiar el resultado. Pero antes hay que insertar un par de referencias para dos nuevos controles, DatePicker y DataForm:

 

xmlns:controls="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls"
xmlns:dataControls="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls.Data.DataForm.Toolkit"

 



    
        
            
                

            

            
                
                    
                    
                    
                
            

            

            
                

 

La primera sección delimitada por el control Activity es básicamente la misma que teníamos antes. La segunda parte contiene el formulario que presenta los detalles de la línea en particular elegida en la cuadrícula. El código tal y como está aquí produciría errores de compilación ya que hace falta definir el estilo DetailsStackPanel.

 

 

Al ejecutar la aplicación se ve la presentación tradicional principal-detalle:

 


Al seleccionar un héroe en la cuadrícula, sus detalles son presentados en el formulario.

 


Al hacer cambios, el ítem recibe una marca de modificado.

 

Nótese también cómo empleados con modificaciones son marcados con el asterisco junto al título del formulario. Los cambios permanecen pendientes hasta que se envíen al servidor. De hecho, empleados que son devueltos a su estado original al deshacer los cambios no serán incluidos en el envío. Para mandar los cambios con el servidor, hay que implementar la respuesta al evento clic del botón Aceptar. Primero se confirman los cambios y luego se envían. Una vez mandados, las marcas de artículo modificado desaparecen.

 

private void BotonAceptar_Click(object sender, System.Windows.RoutedEventArgs e)
{
    Formulario.CommitEdit();
    dds.SubmitChanges();
}

 

Pero todavía no hemos lidiado con verificar que los cambios son aceptables. Algo interesante es que, por decirlo así, el formulario provee verificación gratuita de tipos. Por ejemplo, señalará un error al digitarse letras en un campo correspondiente a un número entero.

 


El formulario está preconfigurado para verificar el tipo de datos digitados.

 

Comprobaciones más sofisticadas tan sólo necesitan con un poco de código adicional. El lugar preciso para describir las reglas de verificación es en SuperEmpleadoService.metadata.cs en el servidor. La razón por ir tan atrás es doble: 1) se pueden comprobar las actualizaciones en la interfaz al usuario, proveyendo una mejor experiencia de uso, y 2) también en el servidor, asegurando la integridad de la información. El siguiente es un ejemplo aspectos que se pueden verificar. Por cierto, para el género, la regla queda sin modificar al español pues los datos en la tabla están en inglés.

 

[ReadOnly(true)]
public int EmpleadoId;

[RegularExpression("^(?:m|M|male|Male|f|F|female|Female)$",
    ErrorMessage = "El género debe ser 'Male' o 'Female'")]
public string Genero;

public Nullable Modificado;

[Required]
[StringLength(100)]
public string Nombre;

[Range(0, 1000, ErrorMessage = "Números deben ser entre 0 y 100")]
public Nullable Numeros;

 

Con tan sólo regenerar la solución se habilitan mucho más interesantes verificaciones en los cambios introducidos por el usuario:

 


Usando la meta-información, se pueden obtener verificaciones más sofisticadas.

 

Nótese cómo al seleccionar errores en el compendio, el campo correspondiente recibe el enfoque. Bueno, suficiente ya de actualizaciones. Démosle atención ahora a cómo añadir nueva información utilizando un nuevo control, ChildWindow, o Ventana Secundaria, de Silverlight 3. Es similar al UserControl usado como fundamento de las páginas de Silverlight, por lo que es apropiado colocarlo en la carpeta Views mediante clic derecho y escogiendo Agregar->Nuevo Elemento y luego Ventana Secundaria de Silverlight.

 


Creando una nueva ventana secundaria que puede actuar como ventana de diálogo.

 

Como la ventana se crea con una configuración pre-establecida, podemos al principio ocuparnos nomás en abrirla desde la página Home.xaml.

 

 

private void BotonNuevo_Click(object sender, System.Windows.RoutedEventArgs e)
{
    var v = new AñadirNuevo();
    v.Show();
}

 


Ventana secundaria con la configuración inicial.

 

La ventana viene lista con botones Aceptar y Cancelar y con un botón de cierre en la esquina superior derecha. Falta únicamente ponerle un control DataForm que, por usar el mismo modelo que el formulario de actualización, tiene casi las mismas características.

 


    
        
            
                
                    
                
                
                    
                
                
                    
                
                
                    
                
                
                    
                
                
                    
                
                
                    
                    
                
                
                    
                
            
        
    

 

En el código subyacente hay que declarar un elemento de tipo SuperEmpleado y crearlo en el constructor. También es tiempo de añadir el código para el botón Aceptar.

 

public SuperEmpleado EmpleadoNuevo { get; set; }

 

En el constructor:

 

EmpleadoNuevo = new SuperEmpleado();
EmpleadoNuevo.Modificado = DateTime.Now;
Formulario.CurrentItem = EmpleadoNuevo;

 

Y el evento clic del botón OK:

 

private void OKButton_Click(object sender, RoutedEventArgs e)
{
    Formulario.CommitEdit();
    this.DialogResult = true;
}

 


Ventana secundaria con formulario de registro de nuevos empleados.

 

Por supuesto, para que el nuevo empleado sea registrado permanentemente hay que enviar los datos al servidor. La ventana principal toma nota del nuevo empleado al ser confirmado por el usuario.

 

private void AñadirNuevo_Closed(object sender, EventArgs e)
{
    var v = sender as AñadirNuevo;
    var contexto = dds.DomainContext as SuperEmpleadoContext;
    if (v.DialogResult == true)
    {
        contexto.SuperEmpleados.Add(v.EmpleadoNuevo);
    }
}

 

La información del nuevo empleado partirá al servidor cuando se presione el botón de Enviar.

Etiquetas asignadas:
 

2 Respuestas a “Ejemplo de desarrollo de sistemas empresariales con Silverlight 3 y servicios RIA en .NET – Parte 2”

  1. Facundo dice:

    Muy buenos los tutoriales. Pero no logro encontrar la forma de mostrar en la grilla información de otra entidad. En vez de un “idCategoria” mostrar la descripción de dicha categoría.
    GRACIAS!

Responder



Licencia de uso

El contenido de las traducciones está sujeto a los términos de protección de derechos de uso de los autores originales quienes han autorizado su publicación en este blog. Asegúrese de entender los terminos de la licencia de cada autor antes de usar tal contenido.

Mis propios artículos son publicados bajo los términos de la Licencia Reconocimiento-Compartir bajo la misma licencia 3.0 Estados Unidos de Creative Commons:

Creative Commons License
Blog de Maromas Digitales by Maromas Digitales, LLC is licensed under a Creative Commons Reconocimiento-Compartir bajo la misma licencia 3.0 Estados Unidos License.

License

The contents of all translated articles is subject to the copyright and licensing terms of the original authors and has been published here with their express permission. Verify the original author's licensing terms before using the contents of these articles.

My own articles are licensed under the Creative Commons Attribution-Share Alike 3.0 United States License:

Creative Commons License
Blog de Maromas Digitales by Maromas Digitales, LLC is licensed under a Creative Commons Attribution-Share Alike 3.0 United States License.