Nivel intermedio

Entity Framework (V): Enlazado y desenlazado de entidades. Estados


Una vez que tenemos claro que un DbContext es el encargado de velar por la integridad en el esquema objeto-relacional, ¿qué ocurriría si manejamos objetos que, por alguna razón, escapan a su control? Nos referimos, por ejemplo, al envío de los datos de un cliente a través de un formulario web y ponernos a la espera de que el usuario los modifique en otra página distinta. Este escenario implicaría, con toda seguridad, que el DbContext en el que se recuperó el objeto sea distinto al DbContext que se encargará de modificarlo.

Por lo tanto, tendremos dos opciones a la hora de tratar este problema:

  • Volver a recuperar el objeto completo, copiar todos los datos del objeto enviado por el usuario al objeto que acabamos de recuperar y guardar los cambios.
  • Enlazar directamente el objeto al DbContext y decirle que actualice los cambios.

Para la primera opción no es necesaria mucha explicación, puesto que se trata de una modificación normal y corriente. Para la segunda opción habrá que trabajar un poco más, pero se considera una opción más correcta. Además, el proceso dependerá de si estamos haciendo uso de un ObjectContext (versión anterior a 4.1) o un DbContext (versión posterior a ésta).

(más…)

Entity Framework (IV): Mapeo mediante procedimientos almacenados


En el anterior artículo aprendimos cómo mapear un procedimiento almacenado en nuestro Entity Model y hacer uso de él mediante una llamada explícita para eliminar en cascada todos los elementos asociados tanto a los clientes como a los pedidos.

Entity Framework proporciona la opción de personalizar los métodos de inserción, eliminación y actualización a través de procedimientos almacenados, haciendo que éstos se realicen mediante la sintaxis natural de Entity Framework pero permitiéndonos a nosotros indicar, a través de procedimientos almacenados, la forma de hacerlo.

Es importante saber que Entity Framework tiene una política de todo o nada para hacer uso de esta característica, es decir: si se decide utilizar un procedimiento almacenado para realizar las eliminaciones, será obligatorio definir también los métodos de inserción y actualización.

Ya definimos antes un procedimiento para las eliminaciones, llamado sp_Cliente_DeleteCascade. Codificaremos ahora un procedimiento almacenado para realizar las inserciones y otro para las actualizaciones.

El procedimiento de inserción recibirá como parámetros los componentes del registro salvo el identificador, que será generado automáticamente. Además, devolverá como valor de retorno la llamada al método SCOPE_IDENTITY(), que almacenará el valor del IdCliente generado para el registro insertado. A este valor le daremos un nombre para poder identificarlo en nuestro mapeo, por ejemplo, “id”.

(más…)

Entity Framework (III): Select, Insert, Update, Delete


Pese a que LINQ to Entities es bastante parecido a LINQ to SQL, a la hora de trabajar con ambas tecnologías es necesario conocer las diferencias más importantes a nivel práctico (dejaremos los fundamentos teóricos a un lado). Ambas tecnologías tienden a la convergencia, puesto que Microsoft está intentando coger lo mejor de cada una de ellas y adaptarlo a ambos mundos. Sin embargo, siguen existiendo diferencias importantes, especialmente en las primeras versiones de LINQ to Entities.

Select. Carga diferida explícita.

Las primeras versiones de Entity Framework contenían ciertas carencias que, con posteriores actualizaciones, han sido solventadas y corregidas. Una de las carencias más importantes se correspondería con la imposibilidad de las versiones anteriores a 4.1 de realizar una carga automática de los elementos referenciados por un objeto. Es decir, si tenemos el siguiente código:


            // Instanciamos el contexto
            var contexto = new testdbEntities();

            // Lanzamos una consulta
            var clientes = from cliente in contexto.Clientes
                           select cliente;

            // Recorremos los clientes
            foreach (Cliente cliente in clientes)
            {
                Console.WriteLine(string.Format("ID: {0}\tNOMBRE: {1}\tAÑO NAC: {2}",
                    cliente.IdCliente, cliente.Nombre, cliente.FechaNacimiento.Year));

                // Recorremos los pedidos
                foreach (Pedido pedido in cliente.Pedidos)
                {
                    Console.WriteLine(string.Format("\tPEDIDO: {0}\tFECHA: {1}",
                        pedido.IdPedido, pedido.FechaPedido));

                    // Recorremos las líneas de pedido
                    foreach (LineaPedido linea in pedido.LineasPedido)
                    {
                        Console.WriteLine(string.Format("\t\tPRODUCTO: {0}\tCANTIDAD: {1}\tTOTAL: {2}",
                            linea.Producto.Descripcion, linea.Cantidad, (linea.Producto.Precio*linea.Cantidad)));
                    }
                }
                Console.WriteLine(" -----------------------------------------\n");
            }

 

(más…)

Entity Framework (II): ObjectContext y Entity SQL


Una de las mayores ventajas de Entity Framework es que es una tecnología agnóstica respecto a la base de datos que tiene por debajo. Con ADO.NET era necesario utilizar clases específicas para cada base de datos (SqlCommand para SQLServer, OracleCommand para Oracle, etc.). Sin embargo, Entity Framework no se casa con nadie: hace uso de elementos genéricos (EntityConnection, EntityCommand, etc.) que genera instrucciones en un lenguaje intermedio denominado Entity SQL, que es muy similar al lenguaje SQL estándar.

En última instancia, el ADO.NET de toda la vida estará trabajando por debajo, pero Entity Framework establece una capa de abstracción superior que evita la necesidad de atarnos a una fuente de datos concreta. No obstante, es posible descender un poco en el nivel de abstracción y, en lugar de hacer uso de LINQ to Entities tal y como hicimos en el artículo anterior, lanzar directamente consultas sobre el ObjectContext usando para ello Entity SQL.

Dependiendo de la versión de Entity Framework que se esté usando, esto se realizará de un modo u otro, ya que la versión 4.1 introdujo como novedad la utilización del DbContext por defecto, envolviendo el ObjectContext dentro de él.

Por lo tanto, si queremos acceder al ObjectContext del DbContext que el diseñador habrá generado por nosotros, será necesario realizar un casting explícito a IObjectContextAdapter y acceder a su propiedad ObjectContext, tal y como mostramos en el siguiente ejemplo, en el que se ejecuta una sentencia Entity SQL en la que se recuperan todos los clientes de la tabla Clientes.


            // Instanciamos el DbContext
            var dbContext = new testdbEntities();

            // Extraemos el ObjectContext del DbContext (a partir de Entity Framework 4.1)
            var objectContext = ((IObjectContextAdapter)dbContext).ObjectContext;

            // Ejecutamos una sentencia de Entity SQL que recupere todos los clientes
            string sqlQuery = "SELECT VALUE c FROM Clientes AS c";
            var clientes = new ObjectQuery<Cliente>(sqlQuery, objectContext);

            foreach (Cliente cliente in clientes)
            {
                Console.WriteLine(string.Format("ID: {0}\tNOMBRE: {1}\tAÑO NAC: {2}",
                    cliente.IdCliente, cliente.Nombre, cliente.FechaNacimiento.Year));
            }

El nombre de la tabla Clientes está en plural porque es el nombre que recibe la propiedad en la clase de contexto generada por el editor, no porque sea el nombre de la tabla de la base de datos. Recordemos que, Entity SQL realiza consultas sobre entidades, es decir, objetos, no sobre la base de datos.

Nuevamente, comprobamos que la consulta recupera los datos correctamente.

Entity Framework (I): Creación de un Entity Model


Entity Framework es la evolución natural de ADO.NET hacia el tratamiento de los datos almacenados en una base de datos relacional a través del paradigma objetual. Por lo tanto, se trata, al igual que LINQ to SQL, de un mapper objeto-relacional que es capaz de abstraer las tablas y columnas de una base de datos y tratarlas como objetos u entidades y relaciones.

Una entidad, por tanto, no es más que un objeto persistente, que cumple las condiciones de unicidad e identidad.

Añadiendo un Entity Data Model

Lo primero que deberemos hacer una vez que hayamos creado nuestro proyecto (por ejemplo, de consola) será añadir un nuevo Entity Data Model. Para ello haremos click derecho sobre nuestro proyecto y seleccionaremos la opción Add > New Item…

Una vez en el diálogo de selección, navegamos hasta la sección Data y seleccionamos el elemento ADO.NET Entity Data Model, al que le daremos un nombre identificativo.

(más…)

LINQ to SQL (III): Consultas compiladas, ejecución SQL y procedimientos almacenados


Compilación de consultas

En ocasiones podemos encontrarnos con consultas LINQ cuyo coste computacional puede ser alto, haciendo que el sistema se sobrecargue. LINQ to SQL proporciona algunas herramientas que mitigan, dentro de lo posible, el impacto de este tipo de operaciones sobre el rendimiento. Una de ellas es la compilación de consultas.

Es posible precompilar una consulta LINQ, de modo que durante su primera invocación realice un plan de ejecución que pueda mantenerse cacheado para ser reutilizado posteriormente. Así, la siguiente consulta:


            int idCliente = 2;

            var consulta = (from cliente in dbContext.Cliente
                            where (cliente.IdCliente == idCliente)
                            select cliente).FirstOrDefault();

Podría ser compilada y almacenada en una variable, que podrá ser reutilizada posteriormente como si fuese un método normal y corriente. Vemos, además, que la consulta puede parametrizarse, de modo que es posible asignarle cierto grado de generalización.


            var getClientePorId = CompiledQuery.Compile(
                (Testdb contexto, int id) =>
                    (from cliente in contexto.Cliente
                     where (cliente.IdCliente == id)
                     select cliente).FirstOrDefault()
                );

(más…)

Sentencias en LINQ (VIII): Cuantificadores, generadores y conversores


Hasta ahora hemos visto las operaciones más comunes que pueden realizarse mediante LINQ. Por lo tanto, sólo nos queda echarle un vistazo a un subconjunto de sentencias de carácter más auxiliar, tales como la cuantificación, generación y conversión de elementos.

Any

El método Any toma como argumento un delegado o expresión lambda que tomará como parámetro un elemento de la lista y devolverá un valor booleano que indicará si al menos un elemento de la lista cumple la condición especificada.

Así, si quisiéramos saber si se ha vendido al menos un producto cuyo ID sea 3, podríamos optar por, bien crear un método que realice la comparación y que será pasado como delegado, bien utilizar una expresión lambda que realice la misma operación.


        // OPCIÓN 1: Usamos un delegado de una función que toma un elemento del listado como parámetro
        // y devuelve un booleano que devuelve el resultado de la comparación
        public bool PedidoConProductoIgualA3(LineaPedido linea)
        {
            return linea.IdProducto == 3;
        }
        bool existe = DataLists.ListaLineasPedido.Any(PedidoConProductoIgualA3);

        // OPCIÓN 2: Usamos una expresión lambda que realice la misma operación
                  bool existe = DataLists.ListaLineasPedido.Any(linea => (linea.IdProducto == 3));

(más…)