IDbSet, IObjectSet como repositorios o tus propias abstracciones

La verdad es que llevo algunos dias pensando en si escribir o no este post, y, por fin, me he decidido a hacerlo. Tal y como se puede sobreentender por el titulo del post, la idea es hablar sobre el uso de elementos como IDbSet ( o sus correspondientes en versiones anteriores ) en el caso de EF como repositorios o bien hacer alguna abstracción sobre este patrón. La idea, surge después de leer algún que otro post sobre este tema y también algunos comentarios que me llegan de twitter ( de personas que, por cierto, bien podían preguntar directamente, pero esto es otro tema ..). Lógicamente, como todo lo que digo y cuento en mi blog esta es una opinión personal y yo no tengo/creo tener, al contrario que otros, una patente de corso por la cual todo lo que haga o diga sea correcto.

Entremos en materia. Yo no voy a negar el hecho de que IDbSet es realmente un repositorio, de hecho, una implementación buenísima gracias a todo el trabajo que podemos hacer con árboles de expresiones, puesto que este elemento, cumple todos los criterios que marca este patrón tal y como lo describe Fowler aquí. Por lo tanto, dicho esto, tranquilamente podríamos hacer uso de los diferentes elementos expuestos por nuestras unidades de trabajo generadas por EF o hechas por nosotros si usamos Code First en aquellos puntos donde fuese necesario. Hacer esto, desde luego es una simplificación, puesto que cierto código que podriamos escribir ya lo tenemos de forma gratuíta y por lo tanto, la pregunta es porque hacer una abstracción sobre este concepto cuando en realidad ya lo tienes implementado ( lo mismo se podría aplicar a otros ORM). La respuesta a esta pregunta no es fácil, de entrada, yo diría que nunca, excepto cuando tengas algún escenario dónde esta abstracción te pueda suponer una serie nuevas de ventajas que sean interesannes para tu solución. A continuación, intentaré enumerar algunos elementos que podrían hacer inclinar la balanza en el hecho de usar o no una abstracción para representar un repositorio.

 

  • Las atribuciones de cada agregado: Cuando se definen repositorios, y por lo tanto, los agregados de un dominio, también se definen las atribuciones que cada uno de estos elementos va a tener. De tal forma, que, no todos los agregados tienen porque tener las atribuciones de los demás. Es decir, pueden existir agregados dónde no puedas realizar ciertas operaciones de mantenimiento de datos o no puedas realizar determinadas consultas. El uso de un elemento como IDbSet<T> no facilita esta tarea para nada, puesto que el consumidor no tendrá una forma de ver una “restricción” de las operaciones que este agregado puede tener, ni siquiera, de hecho, puedes impedir que una operación ( que no forma parte de sus atribuciones ) pueda ser realizada. Lógicamente, el hecho de poner una capa de abstracción sobre la definición de los repositorios de cada agregado es una forma de poder dar y dejar “en un contrato” cuales son las atribuciones que cada agregado tiene.
  • Los repositorios de los agredos no tienen porque ser solamente genéricos, es decir, estos repositorios pueden tener operaciones de consulta importantes dentro del dominio y querer que estas se muestren de forma explícita a los consumidores. De esta forma permitimos que el consumidor del repositorio se pueda desacoplar del criterio de consulta que una operacion del repositorio pueda tener, permitiendo un mejor mantenimiento en caso de cambio de este criterio de consulta. Por supuesto, este problema se podría minimizar haciendo uso del patron specifications pero también la abstracción podría dar un soporte mejor para esto.
  • Cuando uno define un agragado raiz y define las entidades que forman parte de este agregado en realidad también se está decidiendo como será la carga de ciertas asociaciones entre entidades. En ocasiones, queremos/necesitamos imponer que una raiz se carge siempre con ciertos elementos del agregado, por ejemplo, simplemente de caracter didáctico, siempre que carguemos un Order queremos incluir sus detalles. Hacer esto implica establecer de forma explítica un Include en todos los sitios de nuestros código dónde manejemos este agregado raiz. Puesto que el negocio cambia y el dominio por lo tanto también, esto, tiene ciertas implicaciones de mantenimiento, puesto que no es algo localizable de forma directa. El hecho de poner una abstracción nos permite tener un solo punto dónde estos criterios de carga se esetablezca y que por lo tanto, el mantenimiento sea mucho más sencillo y adaptable.
  • Incluir conceptos para la monitorización o la salud es mucho más sencillo cuando puedes escribir código en tus abstracciones, antes, durante o después de las operaciones del repositorio.
  • IDbSet es más, es decir, esta interfaz posee más conocimiento de lo que realmente nosotros necesitamos cuando hacemos uso de los repositorios. De hecho, tiene conocimiento por ejemplo sobre el tracking de los objetos, algo que no es su responsabilidad ( sino de la unidad de trabajo ) y por lo tanto nos enseña realmente más de lo que necesitamos sabes y por lo tantoes una vía abierta para introducir otros problemas.

Seguramente que me dejo algo por cada lado, aún así, yo creo que más o menos ya nos podemos dar una idea de lo tenemos con una o con otra opción. Lógicamente, debemos ser cada uno de nosotros los que elijamos intentando entender las dos opciones y cual va a ser para nuestro desarrollo la mejor, es nuestra REPONSABILIDAD  y por lo tanto hay que tomar la decisión valorando bien pros/contras.

 

P.D; Para el siguiente post intentaré hablar sobre otro tema del que se habla y es si poner un contrato base para nuestras unidades de trabajo o no…

Saludos

Unai

EF 4.1 y la pre-generación de vistas

La verdad es que el tema de la pregeneración de vistas ya es algo sobre lo que he hablado bastante en mi blog, la ultima entrada creo que fué esta referida a temas de rendimiento, algo sobre lo que afecta mucho, sobre todo cuando hablamos de modelos grandes (  en los tiempos de carga y/o de primeras operaciones ). Generalmente, la forma más sencilla de precompilar estas vistas era por medio de una plantilla de T4, como la siguiente, que se encargaba de leer la información de nuestro modelo EDM y generar para esta información un archivo .cs que contenía la pregeneración de vistas y que incluiamos en la compilación de nuestra unidad de trabajo. Si utilizamos Code First, esta plantilla ya no nos servirá ( por lo menos totalmente ) puesto que ya no tenemos un fichero EDMX sobre el que esto trabaje y por lo tanto no tenemos forma de pregenerar estas vistas tan facilmente. Para ayudarnos, el equipo de EF hace un tiempo que saco una herramienta conocida como EF 4.1 Power Tool la cual dispone de una opción para hacernos este trabajo muy sencillo como se puede observar en la siguiente figura, en la que vemos un nuevo menú contextual dentro de Visual Studio con la opción “Optimize Entity Data Model”, opción que automáticamente agregarla un fichero de precompilación de vistas en el proyecto en el que estemos.

 

out1

 

Para el que no quiera usar esta herramienta, esta en CTP1 y a muchos no les gusta ( no nos gusta ) instalar betas y demás en nuestros equipos de trabajo real, podemos utilizar el siguiente fragmento de código, para pregenerar las vistas, lógicamente, esta opción es mucho más “manual” que la anterior, pero aunque sea por un mero ejercicio práctico aquí os lo dejo.

 

Saludos

Unai

Testing y EF 4.1

Hace ya un tiempo que en este mismo blog hablé de como con EF 4.0 podíamos construir tests de nuestros repositorios. En esta ocasión, me gustaría repetir cierto contenido de esa misma entrada aplicándolo a EF 4.1 y ampliandolo con algún tip que nos permita facilitar no solamente los ‘mocks’ sino también las pruebas contra la base de datos y su posible repetición. Algunos de los elementos/patrones que mencionaremos en este post los daremos por sabidos, no obstante, seguro que puede encontrar en la web mucha y muy buena información acerca de los mismos que le serán de ayuda.

Empezaremos por un ejemplo sencillo, partiremos de una unidad de trabajo similar a la siguiente:

 

Parece claro, que si este componente es una dependencia de cualquier otro elemento, por ejemplo un repositorio, no vamos a poder testear estos de una forma aislada, por lo menos no de la forma más sencilla posible, y por lo tanto, sería bueno establecer, para esta unidad de trabajo un contrato que nos sirviera realmente como la abstracción de nuestra unidad de trabajo. Además, si este contrato podemos hacer que no se base en la implementación del queryable de EF, DbSet en EF 4.1, sería mucho mejor. Es decir, buscamos algo como lo siguiente:

Ahora, si vasamos nuestras dependencias en este contrato ya podríamos realizar nuestras pruebas simulando el comportamiento de esta unidad de trabajo. Al principio, puede parecer osado realizar una simulación de DbSet, sabiendo que este es un queryable y por lo tanto un  “interprete” de árboles de expresiones. Sin embargo, veremos como en muy pocas lineas podemos crear un elemento que nos permita representar un IDbSet en memoria.

 

Este elemento del que hablamos, nosotros le llamaremos MemorySet, no es más que la implementación de un IDbSet<T> basada en una lista genérica, la cabecera de esta clase sería algo similar a :

 

Al implementar IDbSet, estamos obligados a establecer el comportamiento para métodos como Add,Attach,Find,Remove etc… todos ellos perfectamente asignables al trabajo con una lista genérica. Quizás en lo que podamos tener más dudas es en la implementación de los elementos específicos de IQueryable, mostrados a continuación:

 

Esta implementación, la podemos realizar fácilmente gracias a un método extensor poco conocido como es AsQueryable,  método que nos ofrece cualquier elemento IEnumerable<T> gracias al cual podemos transformar cualquier enumerable a un elemento queryable.

Una vez terminada la creación de nuestro MemorySet, ya estamos entonces, en disposición de realizar nuestra simulación de nuestra unidad de trabajo en nuestros tests. Llegados hasta aquí seguramente alguno esté preguntando, bueno, bien, ¿pero que pasa con los test hacia la base de datos, los tenemos que hacer o solamente los simulamos?. La verdad es que esto es buena pregunta, porque mezcla diferentes aspectos, por un lado los teóricos que considerarían esto como un test de integración y por el otro lado el pragmatismo… ( hace ya bastante de esto Rodrigo Corral escribió un buen post ). Asumiendo que por un motivo u otro ( un buen motivo es que no todos los elementos de LINQ están soportados en EF y por lo tanto, consultas válidas sobre nuestras simulaciones no tienen porque traducirse de forma correcta) vamos a hacer pruebas con la base de datos, estas deberian asegurarnos ciertos elementos esenciales, como por ejemplo que las pruebas sean repetibles, es decir, que si ejecutamos dos veces seguidas una misma prueba el comportamiento no cambia. Esto, con bases de datos, puede ser más o menos dificil de conseguir, de hecho, hay diferentes patrones para ponerlo en práctica, muy bien explicados y documentados en XUnit Patterns. Como muchos sabréis, la llegada de EF 4.1, además de todo lo que tiene que ver con Code First incluye algún elemento importante que podría facilitarnos esta tarea. Elementos como la generación automática del esquema o los inicializadores nos permitirán de una forma más que sencilla hacer que nuestros test contra una base de datos sean simples de construir y repetibles (asumiendo que hacemos Database Sandbox).

Los inicializadores, no son más que elementos de tipo IDatabaseInitializer<TContext> gracias a los cuales podemos ejecutar un determinado código durante la inicialización de la base de datos con la que esté configurada para trabajar una unidad de trabajo. Por defecto, EF 4.1 nos proporciona tres inicializadores, DropCreateDatabaseAlways, CreateDatabaseIfNotExist,DropCreateDatabaseIfModelChanges. Los nombres seguro que le son completamente auto descriptivos. Estos inicializadores, se establecen dentro de la clase estática Database por medio de su método SetInitializer como puede verse en la siguiente linea:

Los tres inicializdores anteriores ofrecen un método virtual llamado Seed, gracias al cual, además del borrado [y generación] de la base de datos, podemos establecer un conjunto de datos de inicialización. En las siguientes lineas puede ver un ejemplo de una creación de un inicializador para la unidad de trabajo definida anteriormente, fíjese como no le costaría hacer que los mismos elementos con los que construye su mock fueran los elementos con los que inicializa la base de datos.

 

Ahora, una vez que ya sabemos como establecer un inicializador a las unidades de trabajo de EF, solamente nos queda ver como podemos hacer que este se establezca antes de ejecutar cualquiera de los tests que tenemos en un proyecto y, por lo tanto, genere y pueble la base de datos antes de pasar cualquier prueba. Esto, con MSTest es realmente sencillo, bastaría con crear un Assembly Initialize, es decir, un método que la infraestructura de MS TEST ejecutará cuando el ensablado de test sea inicializado ( antes de pasar cualquier prueba por lo tanto )

 

En NUnit, sino me equivoco AssemblyInitialize se corresponde con la definición de un SetupFixture fuera de cualquier namespace, así quiero recordar que era, lógicamente esto no lo puedo asegurar y además es probable que haya cambiado …

 

Espero que esta entrada os haya resultado de interes..

 

Saludos

Unai Zorrilla Castro

Novedades en EF Jun 2011 CTP

Se acaba de hacer pública la primera CTP de la siguiente versión mayor de EF, recordad que ahora mismo estamos en EF 4.1 y que seguirán a esta nuevas versiones menores, 4.X, todas ellas compartiendo la base de EF 4.0. Esta CTP, por el contrario supone un cambio en la libreria base y por lo tanto, la instanción y el trabajo no es tan sencillo, incluso la desinstalación es complicada, por lo que, como siempre decimos, lo mejor es hacer uso de maquinas virtuales de usar y tirar… La información sobre esta CTP está publicada en el blog del equipo de producto.

 

De entre las principales novedades podemos ver el soporte para tipos espaciales, TVF de Sql Server  y por lo tanto la posiblidad de usar cosas como Full Text Search, el uso de multiples results sets como retorno de un SP o el soporte para Enums (me guardo mi opinion extensa,pero esto no está resuelto “como yo creo que se debería, pero esta es mi más que humilde opinión”) .

 

En los siguientes enlaces apunto a los walkthrougs de cada una de las features:

Enums

Spatial

TVF

M Result Sets

 

Ahhh, me olvidaba de una de las más imporantes, por lo menos para mi… que son las autocomplided LINQ Queries. Novedad que nos proporcionará un mecanismo de compilado automático de nuestras consultas LINQ sin necesidad de usar CompiledQuery.Compile como hasta ahora.  Con esta novedad han agregado una configuración más a nuestras ContextOptions llamada ( por ahora) DefaultQueryPlanCacheSettting dónde podemos activar o desactivar las consultas autocompiladas.

 

Saludos

Unai