De la base de datos al cliente con WCF RIA Services

Tercera parte de mi serie de artículos Silverlight, MVVM y WCF RIA Services – Notas de un proyecto real.



En la segunda parte de esta serie expuse el método para usar Entity Framework con procedimientos almacenados. Una vez con el modelo de datos listo, lo que queda es hacer las conexiones necesarias para que lleguen al cliente.

Un excelente mecanismo para lograr este objetivo es los Servicios WCF RIA. Mucho se escrito al respecto. Por ejemplo, Brad Abrams tiene una larga serie de 26 artículos discutiendo este tema. De ellos, once están disponibles en español en este blog. También Jorge Levy ha publicado excelentes artículos describiendo WCF RIAcómo enlazarlos con los datos y cómo presentar el resultado en el cliente.

Mi objetivo en esta parte es verificar que el transporte básico de datos funciona correctamente (lo que los anglosajones llaman “sanity check”). Para ello, voy a usar el mecanismo más simple posible para presentar datos en el cliente.


Servicio de datos

El primer paso es agregar una clase que sirva la  información del dominio (Domain Service Class) en el proyecto DataStore.Web. Mi costumbre, en un proyecto más general, es crear una carpeta llamada Services donde reside esta nueva clase. Sin embargo, como DataStore.Web tiene como único propósito de ofrecer el servicio, lo coloqué en el nivel principal, con el nombre CountryInformationService:


EL tipo de servicio a añadir es Domain Sevice Class.


Seguidamente se nos da la opción de asociar el servicio con un modelo conceptual de datos. Puesto que sólo tenemos uno, PopulationEntities es el modelo preseleccionado. Es importante también indicar que queremos que la entidad Country sea reconocida por el servicio. Como en nuestro caso sólo presentamos información al usuario sin dar la posibilidad de alterar los datos, no es necesario habilitar la edición de países. Tampoco necesitamos que se genere una clase asociada de metadatos. En otras aplicaciones, estas clases asociadas permiten especificar reglas de validación que el servicio usa al crear o actualizar entidades con datos introducidos por el usuario.


El servicio debe ser asociado con el modelo de datos conceptual.


Al final tenemos el nuevo servicio en el proyecto DataStore.Web:


Servicio ya generado en el proyecto DataStore.Web.


Gran parte del trabajo de WCF RIA es eliminar el tedio de escribir el mismo código vez tras vez al generar las partes que son repetitivas. Por ejemplo, el servicio recién creado consiste en el siguiente código generado por el asistente (luego de eliminar comentarios y cláusulas using no necesarias):


namespace DataStore.Web
{
    using System.Linq;
    using System.ServiceModel.DomainServices.EntityFramework;
    using System.ServiceModel.DomainServices.Hosting;
    using DataStore.Web.Model;

    [EnableClientAccess()]
    public class CountryInformationService :
             LinqToEntitiesDomainService<PopulationEntities>
    {
        public IQueryable<Country> GetCountries()
        {
            return ObjectContext.Countries;
        }
    }
}


El método generado produce un tipo IQueryable (que en forma simplificada puede considerarse como una consulta de datos diferida) para la entidad Country. Uno de los elementos clave en el código es el atributo EnableClientAccess que le indica a WCF RIA que debe generar en el cliente el código necesario para ejecutar este método.

Para ver el resultado, hay que compilar el proyecto de manera que el código correspondiente sea generado en el cliente. Es probable que, si ponemos atención al proyecto cliente, DataStore, notemos que no hay nuevos archivos. El problema es que el código generado reside en un directorio oculto. Para verlo hay que revelar todos los archivos:


El código generado se encuentra en una carpeta oculta.


El archivo DataStore.Web.g.cs contiene el código equivalente para el cliente del servicio que definimos en CountryInformationService. Una de las razones por la que está oculto es debido a que es generado automáticamente, y cualquier cambio que hagamos al código que contiene será eliminado al compilar el proyecto.


Atando cabos sueltos

La solución original fue creada usando la plantilla básica Aplicación de Silverlight a la que luego añadí un proyecto de tipo WCF RIA Services Library. A pesar de que las plantillas economizan mucho del trabajo, algo que Visual Studio no hizo fue unir los resultados. En otras palabras, el proyecto principal no sabe nada sobre la biblioteca de servicios RIA. Ese paso hay que ejecutarlo manualmente.

Afortunadamente, el procedimiento es fácil. Simplemente se trata de añadir las referencias apropiadas. En el proyecto MD_Ejemplo_MVVM que es el cliente Silverlight, hay que agregar una referencia a DataStore, y en MD_Ejemplo_MVVM.Web una referencia a DataStore.Web.


La biblioteca de servicios RIA debe ser asociada con el proyecto principal.


Después de asociar los proyectos de la biblioteca de servicios RIA con la aplicación principal, todavía queda un paso más. El archivo App.Config en DataStore.Web contiene la siguientes líneas (Por problemas de espacio he reemplazado en el ejemplo ciertas partes de la configuración que son muy largas con puntos suspensivos.):


<?xml version="1.0" encoding="utf-8"?>
<configuration>

  <system.webServer>
    <modules runAllManagedModulesForAllRequests="true">
      <add name="DomainServiceModule"
           preCondition="managedHandler"
           type="..." />
    </modules>
    <validation validateIntegratedModeConfiguration="false" />
  </system.webServer>

  <connectionStrings>
    <add name="PopulationEntities"
         connectionString="..." />
  </connectionStrings>

  <system.serviceModel>
    <serviceHostingEnvironment aspNetCompatibilityEnabled="true"
      multipleSiteBindingsEnabled="true" />
  </system.serviceModel>

  <system.web>
    <httpModules>
      <add name="DomainServiceModule"
           type="..." />
    </httpModules>
  </system.web>

</configuration>


Esta es la configuración que instruye al servidor de web dónde enviar solicitudes del tipo DomainService. El asunto es que, para ser útil, esta información debe residir en el archivo Web.config del proyecto de web principal, MD_Ejemplo_MVVM.Web. La idea es entonces copiar las secciones arriba en los lugares apropiados de Web.config.


El contenido de App.Config en DataStore.Web debe ser insertado en Web.Config del proyecto MD_Ejemplo_MVVM.Web.


El único caso un poco más complicado es con el elemento modules de la sección system.Webserver que ya existe en el Web.Config y tiene que se reemplazado con una versión expandida.


Servicio al cliente

Todo lo anterior ha establecido la infraestructura para canalizar la información desde la base de datos hasta el cliente. El último paso es presentar los datos. Como mi propósito por el momento es solamente verificar que el sistema básico funciona correctamente, quiero hacer el menor esfuerzo posible en crear tal presentación.

WCR RIA Services provee una excelente herramienta para traer datos al cliente con poco trabajo: DomainDataSource. Yo no soy amigo de usar tales controles en código de producción, pero para pruebas es perfecto. En el archivo MainPage.xaml del cliente, MD_Ejemplo_MVVM, inserto uno de estos controles usando el Cuadro de Herramientas.


Insertando el DomainDataSource con el método gráfico.


La ventaja de insertar el control usando el método gráfico es que Visual Studio se encarga de añadir las referencias necesarias.

Todavía es necesario configurar el control para que use el servicio de datos del dominio. Lo malo es que  esta vez no podemos sacar ventaja de la inserción automática de referencias, pero aun así Visual Studio nos da una mano con IntelliSense:


IntelliSense ayuda a insertar la referencia apropiada para usar el servicio de datos.


Luego de configurar  el control, añadir una cuadrícula para desplegar los datos y un par de otros controles para asistir en la presentación, el código en MainPage.xaml se ve como sigue:


<UserControl x:Class="MD_Ejemplo_MVVM.MainPage"
    xmlns="http://schemas.microsoft.com/winf..."
    xmlns:x="http://schemas.microsoft.com/winf..."
    xmlns:d="http://schemas.microsoft.com/expressi..."
    xmlns:mc="http://schemas.openxmlformats.or..."
    xmlns:riaControls="clr-namespace:System.Windo..."
    xmlns:data="clr-namespace:DataStore.Web;assembly=DataStore"
    xmlns:sdk="http://schemas.microsoft.com/winf..."
    mc:Ignorable="d"
    d:DesignHeight="300" d:DesignWidth="400">

    <Grid x:Name="LayoutRoot" Background="White">
        <StackPanel>
        <TextBlock Text="Lista de países" />

        <riaControls:DomainDataSource
                             Name="Dds1"
                             AutoLoad="True"
                             QueryName="GetCountriesQuery">
            <riaControls:DomainDataSource.DomainContext>
                <data:CountryInformationContext />
            </riaControls:DomainDataSource.DomainContext>
        </riaControls:DomainDataSource>

        <sdk:DataGrid
              ItemsSource="{Binding Data, ElementName=Dds1}"/>

        </StackPanel>
    </Grid>
</UserControl>


El TextBlock es no más que para saber si la aplicación es funcional independientemente de si se pudo establecer comunicación con CountryInformationService. Más información sobre el uso de DomainDataSouce y el acceso a datos en general usando RIA Services se puede econtrar en este artículo por Brad Abrams.

Como estamos en modo de pruebas básicas, me voy a saltar la infraestructura MVC de ASP.NET mediante configurar el proyecto de web para que use MD_Ejemplo_MVVMTestPage.aspx como página de inicio. Al ejecutar la solución obtengo es siguiente resultado después de varios segundos:


La aplicación de prueba ejecutando en el navegador.

Etiquetas asignadas:
 

18 Respuestas a “De la base de datos al cliente con WCF RIA Services”

  1. Sergio dice:

    Hola David,
    me he descargado el proyecto en VB pero parece que el archivo ‘CountryInformationService.vb’ no se encuentra dentro del zip.
    Aunque añado esta clase no consigo hacer funcionar la aplicación (tb me ocurre con el proyecto en c# que si que tiene el archivo que he comentado). Me devuelve una excepción …
    He probado a cambiar el connection string de la bd en los archivos Web.config y App.config para que en lugar de apuntar a
    ‘C:\Users\David\Documents\Visual Studio 2010\Projects\MD_Ejemplo_MVVM\MD_Ejemplo_MVVM.Web\App_Data\Population.mdf’ lo hagan a la ubicación correspondiente en mi equipo, pero sigo recibiendo una excepción.
    Cuando sigo tus pasos y creo el proyecto desde cero, no recibo errores pero el control DomainDataSource no devuelve datos a pesar de que la tabla countries efectivamente tiene datos.

    Como siempre gracias por tus artículos
    Un saludo!

    • David Mora dice:

      Sergio:

      Ya vi el problema que describes. Creo que el problema es que los archivos que enlacé con el artículo son versiones incompletas. De aquí a mañana trataré de subir las versiones correctas de los proyectos.

      Por otra parte, me extraña que al crear el proyecto manualmente no recibas los datos en el cliente. Algo que hay que tener en cuenta es que la tabla de países es relativamente grande y toma varios segundos en ser trasladada al cliente. Si tras esperar unos 30 segundos no pasa nada, entonces tenemos un problema más serio. A ver si me cuentas cómo te va y tratamos de solucionar el caso.

    • David Mora dice:

      Al fin tuve un chance de cargar los archivos apropiados.

      El proyecto en Visual Basic contiene el archivo CountryInformationService.vb.
      Ambos proyectos usan la “variable” |DataDirectory| en la cadena de conexión en Web.Config, para evitar líos con la ruta al archivo de datos.

      En todo caso, si todavía se presentan problemas, es bueno ir al modelo, Population.edmx, y actualizarlo usando “Actualizar modelo desde base de datos…” asegurándose de que el archivo Population.mdf es visible en la etapa de conexión. Luego de eso y de regenerar la aplicación, el ejemplo debería ejecutar correctamente.

  2. Paulo dice:

    Muy bueno tus artículos, pero me gustaría saber en la medida de lo posible como realizar el mismo ejemplo pero con un procedimiento almacenado que se generaron en el articulo anterior.

    Saludos

  3. Paulo dice:

    disculpa pero me falto colocar un procediminto almacenado con parametros

    Gracias

    • David Mora dice:

      En esas estoy, trabajando en un par de artículos discutiendo el diseño de la aplicación, y el inicio del modelo. El modelo usa por lo menos un procedimiento almacenado con parámetros. Apenas los tenga listos, los publico.

  4. Luis Alejandro dice:

    Buenos días estoy buscando un articulo que no solamente me muestre como cargar datos con RIA SERVICE sinó como hace la modificacion de las tablas (insertar, modificar o eliminar), y la verdad que todos los articulos, videos etc son iguales, mostrando datos por este medio, podras comentarme como hacer para poder insertar datos con RIA dado que al poner la opcion de editar con el domain service y crearse los metodos de actualizacion cuando hago la instancia no se ven y no puedo realizar estas acciones.
    gracias

    • David Mora dice:

      Luis Alejandro,

      Entiendo tu frustración. En general es un poco más difícil encontrar artículos que analicen o demuestren a fondo el uso del operaciones como inserción y eliminación de registros en la base de datos.

      En realidad tales operaciones no son muy diferentes de las que se llevan a cabo en una aplicación local. Tal vez la operación que ofrece un mayor reto es una inserción cuando hay un a relación de master-detail. Especialmente debido al lío de obtener una referencia a los registros recién creados para poderlos usar en otros nuevos registros relacionados.

      Brad Abrams cubre el tema en su serie sobre RIA Services, específicamente en su artículo sobre consultas densas de datos. Shawn Wildermuth también ofrece un poco de información al respecto en su artículo sobre diseño de aplicaciones en Silevrlight con MVVM, MEF y RIA pero su enfoque es un poco oblicuo y algo escueto.

      Trataré en un futuro de originar o traducir artículos que discutan el tema. Mientras tanto, tal vez te pueda ayudar ver RIA Services como una proyección, en el cliente, del contexto de datos del servidor. RIA Services lleva cuenta de las operaciones que son efectuadas en las tablas y registros presentes en el cliente y luego transfiere tales operaciones al servidor.

      Desde ese punto de vista, si estás usando algo como Entity Framework, o procedimientos almacenados, o algún otro sistema de acceso a datos, lo más importante es proveer esas operaciones en el Servicio de datos. Luego RIA Services se encarga de que estén disponibles en el cliente. El modo de operación es muy similar a si estuvieses usando el WCF básico (que es de hecho, el fundamento de RIA Services).

      • Luis Alejandro dice:

        Gracias
        por la respuesta

        Pero quisiera saber como crear un DomainDataSource desde el Còdigo C#, sin necesidad de crearlo en xaml

        Gracias,

        • David Mora dice:

          Luis Alejandro,

          En el código subyacente, puedes crear una instancia de DomainDataSource, por ejemplo en el constructor, luego de InitializeComponent. Una vez creada, puedes asignar valores a las propiedades relevantes como DomainContext y QueryName. Luego puedes asignar el DomainDataSource a la propiedad ItemsSource en cuadrículas, listas y así por el estilo.

          Más información sobre DomainDataSource: http://msdn.microsoft.com/en-us/library/ee707363(VS.91).aspx

          • Luis Alejandro dice:

            Gracias me ha servido de mucha utilidad
            para que se ejecute la consulta se hace siempre como una llamada asincronica, se puede oviar esta condición?

            gracias

          • David Mora dice:

            No estoy seguro si entendí tu pregunta. Si te refieres a evitar que la llamada sea asincrónica, entonces no. Todas la llamadas al servidor desde el cliente de Silverlight son asincrónicas. Esto no es particular de RIA Services, sino que es parte la arquitectura fundamental de Silverlight.

            Si no es eso lo que preguntas entonces tal vez me puedes ayudar clarificando un poco.

          • Luis Alejandro dice:

            Si eso queria saber
            Gracias

          • Sam dice:

            Y cómo lo utilizo para un SP que devuelve Escalares, y cómo lo llamo?

  5. Arturo dice:

    Hola que tal, están super tus tutoriales. Quisiera saber si tienes pensado mostrar como ejecutar Stored Procedures con valores de retorno y sin. Ojalá que publiques pronto un artículo de eso, en un proyecto en el que estoy trabajando me ha servido de mucho. Gracias!

  6. Juan dice:

    Hola David.

    Me sucedia algo parecido a Sergio, despues de cambiar el string de conexion para que no apuntara a “‘C:\Users\David\Documents…” todo me compila y ejecuta correctamente pero en pantalla no veo el grid con los datos. Solo veo el texto “Lista de paises”.

    Gracias por tu ayuda y excelentes articulos.

  7. Sam dice:

    Amigo, sucede que me interesa saber como ejecutar un SP que de hecho hace lo mismo, me devuelve un 1 o un 0 según sea correcto o incorrecto porque es para autenticar un usuario y contraseña, por lo que también debo mandarle estos parámetros ¿puedes decirme cómo lo hago? ya anexé mi función importación, sólo faltaría saber cómo llamar al SP y asignarlo a una variable para luego verificarla.

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.