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



Esta es la segunda parte de una serie de artículos describiendo mis experiencias al diseñar e implementar una aplicación de Silverlight de la vida real. La primera parte explica mis objetivos y provee una vista a grandes rasgos del sistema.

Tal como mencioné en la parte anterior, el cliente tiene reglas internas que prohíben el acceso directo a las tablas de la base de datos. La única forma de interacción permitida es mediante procedimientos almacenados. Es interesante notar las reacciones de varios desarrolladores al mencionar tal posición. Algunos inmediatamente comienzan a defender el punto de vista de que el acceso a las tablas es esencial y que, de hecho, Entity Framework (o cualquier otra herramienta ORM) es capaz de crear mejores consultas que las que se logran manualmente. Otros responden que no trabajarían con un cliente que imponga semejantes restricciones tiránicas. También hay los que defienden la lógica de usar procedimientos almacenados, citando los beneficios de mantener todo lo relacionado con el acceso a datos en el mismo lugar.

Para mí todas esas discusiones son inútiles en el contexto de esta aplicación. Puesto que es una directriz impuesta por el cliente, es simplemente una realidad a la que, como desarrollador, hay que adaptarse. Especialmente al considerar que tales circunstancias son relativamente comunes en el mundo empresarial. Es como si al contratar a un maestro de obras para que amplíe un cuarto en el segundo piso de mi casa, comenzara a tratar de convencerme de lo conveniente o esencial que es tener un elevador. Podrá ser cierto, y puede que  facilite la labor de los constructores, pero no es para lo que lo contraté.

En fin, el uso de procedimientos almacenados es uno de los pilares en el diseño de esta aplicación, por lo menos en lo que tiene que ver con el acceso a la información. Aun así, considerando que Entity Framework provee facilidades para el manejo de datos y que se adapta bien al uso de WCF RIA Services, mi idea era encontrar la forma de usar la infraestructura con los procedimientos almacenados.

Por dicha, la última versión de Entity Framework ha mejorado mucho su compatibilidad con este tipo alterno de acceso a datos.


Añadiendo el modelo de datos

Hace algún tiemplo expliqué el procedimiento para añadir un modelo de datos basado en Entity Framework. Los pasos en este caso son similares.

Para crear el modelo de datos, agregué una carpeta llamada Model al proyecto DataStore.Web y a éste le añadí un nuevo elemento de tipo ADO.NET Entity Data Model:


El ADO.NET Entity Data Model crea una capa de acceso a datos mediante Entity Framework.


Si han seguido los pasos sugeridos en la parte anterior, verán un cuadro de diálogo como el siguiente:


Estableciendo la conexión. Para más detalles ver el enlace referido en el artículo.


Y luego el siguiente mensaje:


El asistente sugiere copiar la base datos. El texto principal explica por qué no se siguió su consejo.


En el sistema original, la base de datos está en un servidor aparte, dedicado exclusivamente a SQL Server. No obstante, en nuestro ejemplo la base de datos es un archivo mdf que es adjuntado a SQL Server Express. La causa de preocupación del asistente es que la base datos está alojada en un proyecto diferente, MD_Ejemplo_MVVM.Web lo cual no es siempre buena idea. Lo que el asistente no sabe es que ése es el proyecto principal, y DataStore.Web es un DLL. Por eso, la respuesta que yo escogí es No: no quiero copiar la base de datos a este proyecto.

Tal como la figura abajo muestra, la lista incluye procedimientos no relacionados con nuestro proyecto, la idea es escoger solamente los que va a usar la aplicación. A pesar de su nombre, esta sección también lista las funciones (User Functions) disponibles en la base de datos.


El asistente lista todos los procedimientos y funciones disponibles.


Nótese también el nombre escogido para el espacio de nombres. A veces el asistente propones nombres que no tienen sentido o que son más largos que un domingo sin plata. Sea cual sea, el nombre puede ser cambiado más adelante usando el Explorador de modelos y editando el archivo XML que lo describe.

Al terminar con el asistente, se genera el modelo de datos y encontramos dos nuevos archivos en la carpeta Model:


Modelo de datos generado por Visual Studio.


A diferencia de cuando se genera un modelo a partir de tablas, en este caso el área de diseño del modelo está en blanco. La razón es que no se crearon entidades, es decir, representaciones conceptuales del esquema de datos. Los procedimientos almacenados fueron añadidos, pero para verlos se necesita el Explorador de modelos que puede ser invocado al seleccionar Ver->Otras ventanas->Explorador de Entity Data Model o usando el menú contextual del área de diseño:


El explorador de modelos puede ser invocado usando el menú contextual del área de diseño.


El modelo de datos de Entity Framework, Population.edmx es un archivo XML que describe el esquema de la base de datos (por lo menos la parte en la que estamos interesados), el modelo conceptual usado en la aplicación, y la relaciones entre ambos. Aunque es posible editar ese archivo, es mucho más fácil controlarlo usando la superficie de diseño y el explorador del modelo. La superficie de diseño presenta el modelo conceptual, mientras que el explorador provee una  vista más completa.


El modelo de datos representado por el explorador.


La primera sección cubre el modelo conceptual y es un reflejo de los objetos visibles en el área de diseño. La segunda sección, PopulationModel.Store, representa la descripción de las entidades y relaciones contenidas en la base de datos. En la ilustración se pueden ver los procedimientos almacenados. Yendo un poco más adentro se puede ver que los parámetros han sido modelados. De hecho, luego de seleccionar uno de los procedimientos o de los parámetros, se le puede inspeccionar en la pestaña de Propiedades.


Conexión al modelo conceptual

Pero aún no es posible usar los procedimientos almacenados. Cuando modela tablas, Entity Framework crea equivalentes en el modelo conceptual y los ata al modelo físico usando asociaciones o “mappings” entre ambos. En el caso de un procedimiento almacenado, esa tarea se deja al programador.

Lo bueno es que la infraestructura viene con algunas herramientas que aligeran la carga. Para empezar, podemos hacer clic derecho en el procedimiento AuthenticateUser y seleccionar la opción Agregar importación de función.


El asistente se invoca usando el botón derecho del ratón en el procedimiento almacenado deseado.


El asistente es más sofisticado de lo que aparenta. Con esta herramienta se puede incorporar casi cualquier tipo de procedimiento almacenado al modelo conceptual. El procedimiento AuthenticateUser es un buen ejemplo inicial por ser relativamente sencillo.


ALTER PROCEDURE dbo.AuthenticateUser
	(
	@LoginName nvarchar(20),
	@Password  nvarchar(20)
	)
AS
	SELECT COUNT(*)
	FROM [User]
	WHERE [User].LoginName = @LoginName
	  AND [User].Password = @Password


El código devuelve la cuenta de todos los registros con la combinación de LoginName y Password. Puesto que la tabla exige que el LoginName sea único en cada registro, la cuenta sólo puede ser uno o cero. En este tipo de procedimiento, el resultado podría  ser recibido en varios tipos de datos, pero para facilitar las cosas lo vamos a asignar a un int o Integer. Una forma de hacer esto es mediante seleccionar el tipo de datos Escalar en el asistente y luego escoger el Int32. Al hacer clic en el botón OK el asistente asociará el procedimiento con un método que lo represente en el modelo conceptual y que es de tipo Int32.


El asistente permite asignar el tipo de datos devuelto por el procedimiento almacenado.


Sin embargo, no todos los procedimientos son tan simples. Por ejemplo, ¿qué pasa cuando el resultado es un juego de registros con columnas de varias tablas diferentes? Además, daría pereza tener que examinar cada procedimiento para saber qué tipo de datos devuelve. Para evitar ese trabajo extra, se puede usar el botón Obtener información de columna que instruye al asistente a que vaya y examine los datos producidos por el procedimiento.

Volviendo al ejemplo de AuthenticateUser, si primero se pulsa el botón para obtener la información, vemos que devuelve un entero de tipo Int32 que, por cierto, es anulable. Podemos entonces escoger el tipo de datos que es compatible en la lista de escalares.


El asistente es útil para discernir el tipo de datos devueltos por el procedimiento almacenado.


Aún así, no parece gran cosa. Pero un ejemplo un poco más interesante nos ayuda a ver el valor provisto por el asistente.


Para casos de resultados compuestos, el asistente puede generar una clase que corresponda al conjunto de datos devueltos.


La secuencia es:

  1. Se pulsa el botón Obtener información de columna para que el asistente investigue el tipo de resultados devueltos por el procedimiento
  2. El resultado es ahora más complejo, compuesto por varias columnas o campos. Un escalar no es apropiado en este caso; más bien se necesita una estructura de datos o una clase. Es por eso que el botón Crear nuevo tipo complejo es activado.
  3. En vez de crear la clase manualmente, el asistente puede crearla automáticamente basado en el formato de la respuesta obtenida hace un momento. Al pulsar el botón para crear el tipo de datos complejo, el asistente analiza las columnas del registro de resultados y crea una clase compatible.
  4. A la vez que crea la clase, el asistente escoge el tipo Complejo como el tipo devuelto por la función y le da un nombre inicial. El formato dado al nombre es el de la función con el sufijo _Result. Por lo general, yo le asigno un nombre más significativo a la nueva clase. Para este ejemplo, un nombre como Country o CountryDetails puede dar mejor idea del contenido.


Los mismos pasos se pueden aplicar al resto de los procedimientos almacenados. En cada caso, analizando los resultados obtenidos de la base de datos y escogiendo tipos y nombres apropiados para la función que asocia al procedimiento con el modelo conceptual.

En caso de tener entidades en el modelo conceptual (las entidades pueden representar tablas, clases POCO u otros tipos de datos con sus miembros asociados de una forma u otra al modelo de la base de datos), el asistente puede asociar el resultado devuelto por un procedimiento almacenado a entidades mientras ambos sean compatibles.

Es importante recordar que las entidades (basadas en EntityObject) y los tipos complejos (basados en ComplexObject) son tratados de forma diferente por el Entity Framework, y más importante en nuestro caso, por el DomainDataService creado al usar WCF RIA. Específicamente, los objetos basados en ComplexObject:

  • No tienen el concepto de columna o campo identificador o clave.
  • El contexto en el cliente no lleva cuenta de su estado o de cambios efectuados.  Por menos cuando son usados por sí solos.
  • No pueden ser obtenidos desde o enviados directamente a la base de datos.

Para más detalles, les recomiendo consultar el libro Programming Entity Framework, Second Edition, por Julia Lerman (Aunque todavía no está a la venta, si tienen acceso a Safari, pueden consultar la versión preliminar. Si no, parece que el libro va a estar disponible a mediados de agosto).


Procedimientos almacenados y entidades

El modo de uso del ejemplo no incluye la modificación o creación de datos. Es solamente consulta, por lo que los objetos complejos son suficientes sin requerir las ventajas adicionales de las entidades basadas en EntityObject. Sin embargo con el fin de completar la lista de posibles resultados de los procedimientos almacenados, he modificado el modelo ligeramente para usar una entidad.

Con un clic derecho en la superficie de diseño o el explorador del modelo se puede invocar el asistente para actualizar el modelo.


El modelo se puede actualizar usando el menú contextual.


El asistente compara el modelo con la base de datos y permite añadir nuevos elementos al modelo, o sincronizar los ya existentes con posibles cambios en el esquema de datos. En nuestro caso vamos a agregar una entidad representando la tabla Country.


El asistente de actualización del modelo permite agregar elementos de la base datos al modelo.


Al pulsar Finalizar, el asistente crea una nueva entidad con una estructura equivalente a la tabla seleccionada en el modelo conceptual:


Entity Framework crea un representación en código de la tabla en la base de datos.


Si se expande el nodo Tablas/Vistas bajo PopulationMode.Store, se puede ver que la tabla Country está ahora presente en el modelo de almacenaje.

Por cierto, puede que notaran que al añadir la tabla al modelo, le indiqué al asistente que pusiera en plural o singular los nombres de los objetos generados. Si inspeccionamos sus propiedades vemos que Entity Framework tiene cierto conocimiento sobre plurales para sustantivos y lo usa para asignar automáticamente un nombre al conjunto de países. Lamentablemente este es uno de esos casos en que, por el momento, el uso de nombres en español echa a perder la funcionalidad (o tal vez la funcionalidad echa a perder los sustantivos en español :).


Entity Framework entiende plurales en inglés. Desafortunadamente no funciona con nombres en español.


Volviendo al punto, la idea es asociar la entidad con procedimientos almacenados, así que llamamos al asistente usando como objetivo el procedimiento GetCountryDetails y, suponiendo que no estamos seguros del tipo de resultados que provee, usamos la función Obtener información de columna:


Un procedimiento almacenado se puede asociar a una entidad si ambos calzan bien.


Como se puede notar, los resultados calzan con la entidad que acabamos de crear y podemos asignar esa clase como el tipo de resultado usando la opción Entidades y escogiendo Country.

Para aclarar, el orden de los miembros de la entidad no tiene que ser el mismo que el del conjunto de resultados del procedimiento. De hecho, no es necesario que exista una correspondencia uno-a-uno entre ambos. La entidad puede tener más miembros que los que provee el procedimiento o viceversa. De nuevo, el libro de Julia Lerman es un excelente recurso para obtener más detalles.


El resultado final

Luego de aplicar los pasos anteriores al resto de los procedimientos en el modelo, el resultado es:


Procedimiento Tipo de resultado Tipo .Net
AuthenticateUser Escalar Int32
GetContinentCountries Objeto Complejo CountrySelector
GetCountryDetails Entidad Country
GetCountryNames Objeto Complejo CountrySelector
GetPopulationData Objeto Complejo PopulationData
GetUserContinents Objeto Complejo ContinentSelector
IsUserAuthorizedForContinent Escalar Int32


Con el modelo de datos listo, el siguiente paso es hacerlos llegar al cliente Silverlight. Para ello voy a usar los servicios WCF RIA en el siguiente artículo.

12 Respuestas a “Usando Entity Framework con Procedimientos Almacenados”

  1. Chrsitian dice:

    Muy buen tutorial. Estas 2 partes me ayudaron mucho para poder entender mejor el funcionamiento de MVVM.

    Hay una tercera parte ? Tal vez no la encontre todavia.

    Muchas Gracias !!!

    • David Mora dice:

      Christian,

      Me da mucho gusto que los artículos sean de ayuda para entender mejor Silverlight y los conceptos relacionados.

      Acabo de publicar la tercera parte de la serie. En estos días estoy cargado de trabajo y no he tenido tanto tiempo como quisiera para trabajar en el blog. Pero poco a poco voy terminando artículos. Pronto terminaré con las fases preliminares y entraré de lleno en el tema de MVVM.

  2. Sergio dice:

    De nuevo muy claro todo lo expuesto, muchas gracias por esta serie de arículos.

  3. Ahome dice:

    Una consulta, a mi no me sale el boton maravilloso q dice Obtener información de columna.
    Por favor su ayuda.

    • David Mora dice:

      Ahome,

      Qué extraño. Sin embargo la pregunta es demasiado escueta. Un poco más de detalles podrían ayudar a entender mejor lo que está pasando. Tal vez si das cuenta de la secuencia de acciones que has tomado, y las versiones de Visual Studio, .NET y similares…

  4. yense dice:

    Bueno man
    una consulta como puedo consumirlo o pasar todos los datos de mi procedimiento complejo a por ejemplo datatable u otros

    no se si me puedes ayudar en esta parte

    gracias

    • David Mora dice:

      Yense,

      No estoy seguro de entender bien tu pregunta. Yo he usado DataTable en el contexto de conexiones genéricas a la base de datos (usando DbConnection) o con DataSets. La idea de EF es poder trabajar a un nivel de separación más elevado, virtualmente eliminando la necesidad de DataTables o DataReaders, y usando objectos del dominio en su lugar.

      Tal vez mal interpreté tu duda. Si me das un caso más específico puede que sea más fácil ubicarme. :)

  5. Leandro dice:

    Muy bueno tu post! Toda la web en general está impecable! La estoy leyendo de punta a punta

  6. Leandro dice:

    Hola David, muy útiles tus artículos, gracias por tu tiempo.
    Tengo una duda… estoy creando una aplicación de prueba para comenzar a interiorizarme con RIA Services y me surgió la necesidad de agregar una columna a una tabla de la base de datos. Esta tabla ya esta vinculada a un Entity, ¿cómo hago para que se actualicen los datos expuestos por el servicio sin tener que crear otro?
    Gracias

  7. Jose dice:

    Hola David, excelente trabajo.
    Soy nuevo en silverlight y me duda es la siguiente. Tengo en el EF procedimientos alcenados tales como Update o Delete, sin enbargo cuando creo el servicios WCF RIA “no aparecen”. Como puedo visulizar o utilizar estos procedimientos?

    Gracias

  8. carmen dice:

    muy bueno el tutorial, ahora stoy haciendo una investigacion esto me hara de gran ayuda

  9. Nacho dice:

    Hola a todos! :)
    A mi no me aparece la opción “Agregar importación de función” cuando le doy click derecho al procedimiento.
    Sí aparece cuando le doy click en la zona del diagrama y dice “Agregar importación de funcioNES” pero en la ventana en el lugar “Nombre de procedimiento almacenado” no me aparece nada.
    Es raro, el procedimiento lo puedo ver en el explorador de modelo. Tengo la versión Visual Studio 2010 Professional versión de prueba.
    Alguien con el mismo problema?

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.