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

4 comentarios sobre “Entity Framework y la generalización de consultas”

  1. 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
    where T:EntityObject,
    where C:ObjectContext

    Y después lo utilizamos de esta forma:

    public List 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!!!

  2. 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.

  3. 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 getProyectos()
    {
    using (OmanEF2 context = new OmanEF2(EntityConnectionString))
    {
    ObjectQuery
    proyectos = context.PROYECTO;

    /*List 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.

Deja un comentario

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *