Entity Framework y la generalización de consultas

El otro día, dando la charla de Entity Framework (organizada conjuntamente con Microsoft, CIIN y Plain Concepts), surgieron hacia el final de la charla ciertas preguntas con respecto al uso de EF en proyectos reales. La pregunta más destacada y también la más común de las que me han realizado en este tipo de eventos tiene que ver con la generalización de consultas. Es decir, cómo aprovechar las capacidades de EF para no tener que repetir muchos de los métodos de consulta generales para cada una de las entidades. El objetivo es, en definitiva, dado un conjunto de entidades generadas por EDM, cómo hacer para sin apenas esfuerzos tener implementados los métodos de consulta tradicionales como GetAll, GetByFilter, etc. y los métodos de manipulación comunes como Add, Update y Delete. Por supuesto, una de las condiciones indispensables es que todo el trabajo sea tipado; es decir, que para cada una de las entidades dispongamos de estos métodos de consulta generalizados para su tipo específico.

A continuación veremos un esbozo simple de cómo conseguir esto, aunque hay varias alternativas, y por supuesto lo siguiente será un ejemplo sencillo que ofrece muchas más posibilidades. Si se fija un poco en el código generado por EDM, verá como el contexto de trabajo, ObjectContext, dispone de los distintos ObjectQuery<T> sobre los que podemos hacer consultas. Para ello se basa en el método genérico de ObjectContext CreateQuery, al que le pasamos una sentencia de Entity SQL similar a la siguiente:

public global::System.Data.Objects.ObjectQuery<Producto> Producto

{

get

{

if ((this._Producto == null))

{

this._Producto =

base.CreateQuery<Producto>("[Producto]");

}

return this._Producto;

}

}    

 

Fíjese como la expresión e-SQL es el nombre del EntitySet con el que estamos trabajando. Si deseamos crearlo para un EntityType específico, la expresión seria OfType(([Producto]),([Model].[Audio])), por poner un ejemplo.

 

Llegados a este punto, ya podríamos construir una clase similar a la siguiente:

 

public class EntityQuery<T>

where T:EntityObject

{

public string EntitySetName { get; set; }

 

public EntityQuery(string entitySetName)

{

this.EntitySetName = entitySetName;

}

 

private ObjectQuery<T> CreateQuery(ObjectContext context)

{

return context.CreateQuery<T>(EntitySetName);

}

}

 

Si se da cuenta, EntityQuery es una clase genérica, aunque solamente para entidades de EDM, que se inicializa con una expresión determinada que nos permitirá crear un ObjectQuery de un tipo en particular, sobre el cual ya podremos construir nuestras consultas y realizar los trabajos de actualización, inserción, etc. A continuación incluimos el método que nos permitirá obtener todos los elementos de ObjectQuery construido a modo de ejemplo:

 

public List<T> GetAll()

{

using (Entities context = new Entities())

{

ObjectQuery<T> query = this.CreateQuery(context);

return query.ToList<T>();

}

}

 

Bien, ya tenemos una clase EntityQuery genérica a la que agregaremos todas las consultas que pueden aplicarse a las entidades de nuestro modelo; ahora solamente queda particularizarlas. Para ello podemos optar por dos vías: la primera es crear distintas instancias de esta clase especificando el EntityObject particular e inicializando en nombre del EntitySet en su constructor; la segunda consiste en crear herencias de esta clase en las que inicializaremos el valor del nombre del EntitySet llamando a la clase base. Un ejemplo de esta última vía es:

 

public class ProductoQuery

:EntityQuery<Producto>

{

public ProductoQuery()

:base("[Producto]")

{

}

}

 

Con esto, ProductoQuery ya dispondrá sin más trabajo de todos los métodos de consulta implementados en EntityQuery particularizados para ese EntityObject (Producto en nuestro caso), y además también dispondrá de una posible vía para crear las consultas específicas que deseemos tener sobre esta entidad.

 

Fíjese además en que la posible automatización de estas clases es trivial usando cualquier framework de plantillas de los que tenemos disponibles en la actualidad.

 

 

Conclusión:

Lo realizado hasta aquí es un ejercicio simple, que podríamos ampliar mucho más permitiendo el uso de árboles de expresión en los métodos genéricos de EntityQuery, creando un formato de filtros para no tener que conocer e-SQL, y por supuesto aplicando las distintas casuísticas a la hora de actualizar, insertar y borrar elementos del contexto; pero como punto de partida para el que desee implementar una solución genérica creo que es bastante adecuado.

 

Un saludo,

 

Unai

Published 6/7/2008 12:35 por Unai
Archivado en:
Comparte este post:
http://geeks.ms/blogs/unai/archive/2008/07/06/entity-framework-y-la-generalizaci-243-n-de-consultas.aspx

Comentarios

# re: Entity Framework y la generalización de consultas

Excelente post como siempre Unai. Sólo quería añadir, profundizando en esas posibilidades de ampliación del ejemplo que indicas, que podemos llevar la generalización un nivel más allá si hacemos genérico también el contexto:

public  class  EntityQuery<T,C>

where T:EntityObject,

where C:ObjectContext

Y después lo utilizamos de esta forma:

public  List<T> GetAll()

{    using (C context = new C())

    // resto de código...

De esta forma podemos tener una aplicación con varios modelos de Entity Framework y usar siempre el mismo código para esos métodos de manipulación comunes.

Un saludo!!!

Monday, July 07, 2008 1:09 PM por Jose Luis Soria

# re: Entity Framework y la generalización de consultas

Efectivamente, Jose Luis, sobre esto se puede avanzar muchísimo...

Monday, July 07, 2008 1:42 PM por Unai

# re: Entity Framework y la generalización de consultas

Hola, que tanta diferencia existe en lo que respecta a tiempos de respuesta? comparados con los metodos tradicionales (es decir por ejemplo, usando un stored procedure)

Saludos.

Monday, July 07, 2008 9:33 PM por Jersson

# re: Entity Framework y la generalización de consultas

Hola Unai, estoy empezando a jugar un poco en el EF y tengo el siguiente problema,

me conecto a oracle con un proveedor externo(ORANET), genere el EDM y todo bien, el problema surge cuado quiero transformar el ObjectQuery a List<>, arroja el mensaje Error del proveedor subyacente en Open.

este es el código que estoy utilizando;

       public List<PROYECTO> getProyectos()

       {

           using (OmanEF2 context = new OmanEF2(EntityConnectionString))

           {

               ObjectQuery<PROYECTO> proyectos = context.PROYECTO;

               /*List<PROYECTO> p = (from c in proyectos

                                   where c.PROYECTO_ID==1

                                   select c).ToList();*/

               return proyectos.ToList();

           }

       }

me podrias ayudar a encontrar el problema.

de antemano gracias.

Monday, November 24, 2008 3:36 PM por Gerardo