Self-Tracking en Entity Framework 4.0

Una de las nuevas características que se han desarrollado en Entity Framework 4.0, es la posibilidad de usar entidades Selft-Tracking. Las entidades Self-Tracking son clases que no tienen dependencia de Entity Framework, pero que incluyen mecanismos que gestionan los cambios es sus propiedades.

Estas entidades nos permiten enviarlas a un cliente (por medio de WCF, por ejemplo), realizar cambios sobre ellas y recibirlas conociendo que cambios se han realizado, sin necesidad de consultar el contexto. Con estos cambios y con algunas nuevas funcionalidades de Entity Framework 4 se obtiene un mejor rendimiento cuando persistimos las entidades en el contexto.

Cada clase de nuestro modelo implementa la interfaz IObjectWithChangeTracker, que tiene los métodos necesarios para gestionar los cambios (Unchanged, Modified, Added o Deleted), y no sólo gestiona los cambios, sino que también mantiene los valores originales.

Para generar las entidades Selft-Tracking nos tenemos que instalar la CTP2 de Entity Framework, que la podemos descargar aquí.

Una vez instalada la CTP2, tendremos una nueva opción en nuestro modelo (fichero edmx) que nos permite añadir un nuevo elemento de generación de código.

EF_SelfTracking1 

y en la ventana de nuevo elemento, tenemos la opción del generador de entidades Selft-Tracking de ADO.NET (ADO.NET Selft-Tracking Entity Generator)

EF_SelfTracking2

¿Qué hemos conseguido con esto? Bueno, si revisamos nuestro Solution Explorer, podemos observar que se han creado dos nuevos elementos, ClientesModel.Context.tt y ClientesModel.Types.tt.

EF_SelfTracking3

Estos dos nuevos ficheros son plantillas de texto T4 que se utilizan para generar código. Estas plantillas generan las clases necesarias para Selft-Tracking, tanto para el Contexto como para nuestras entidades del modelo. Más información sobre plantillas T4 en el siguiente post.

Ahora tenemos generados el código necesario para nuestro Selft-Tracking Context y para nuestras Entidades Self-Traking, pero ¿es recomendable dejar nuestras entidades en el mismo proyecto que nuestro contexto? Si lo que queremos es desarrollar una aplicación N-tier, la respuesta es no, lo ideal, para poder reutilizarlo en todas nuestras capas, sin dependencias de Entity Framework, sería separar la plantilla ClientesModel.Types.tt en otro proyecto para que pueda ser utilizado en cualquier capa de nuestra aplicación.

Veamos cómo debería de quedar.

EF_SelfTracking4

Al separar nuestras entidades (ClientesModel.Types.tt), tendremos que hacer ciertos cambios en estas plantillas para que la generación y las referencias sean las correctas.

  1. Añadimos en el proyecto Data la referencia al proyecto Model.
  2. Las entidades Self-Tracking se generan con la capacidad de que puedan ser serializadas, necesitaremos añadir la referencia a System.Runtime.Serialization al proyecto Model.
  3. Modificamos la plantilla ClientesModel.Types.tt para que genere nuestro modelo a partir del fichero edmx. Cambia la ruta de la variable string inputFile = @”ClientesModel.edmx” a @”..EfTracking.DataClientesModel.edmx”.
  4. Por último, modificamos la plantilla ClientesModel.Context.tt para que incluya los using necesarios para que utilice nuestras entidades que están en el namespace EfTracking.Model. Buscamos las dos secciones de using que se encuentran en la plantilla y añadimos la nuestra.

Con estos cambios, conseguimos distribuir nuestras entidades, incluyendo la plantilla de generación del código, en otro proyecto (dll) para su reutilización en cualquiera de las capas (cliente, servicio, datos, etc.). Al incluir la plantilla, mantenemos su funcionalidad que nos permitiría modificar nuestro Entity Framework (fichero edmx) y que se reflejen los cambios en las entidades Self-Tracking.

Si nos creamos un servicio que utilice las entidades y el contexto, con Self-Tracking podemos obtener nuestra entidad y realizar la actualización de la misma, utilizando el siguiente método:

   1: public Model.Contact UpdateContact(Model.Contact contact)

   2: {

   3:     using (var context = new Data.ClientesEntities())

   4:     {

   5:         context.Contacts.ApplyChanges(contact);

   6:         context.SaveChanges();

   7:      

   8:         return contact;

   9:     }

  10: }

 

El método ApplyChanges es una nueva funcionalidad que permite enlazar las entidades e interpretar los cambios realizados para persistirlos a nuestro contexto.

Tenemos que tener en cuenta que implementando la funcionalidad de Self-Tracking en nuestra aplicación, limitamos la interoperabilidad de nuestra aplicación ya que no podemos asegurar que la funcionalidad de Self-Tracking sea manejada por clientes no .NET.

Sin embargo, siempre hay que valorar si necesitamos esta nueva funcionalidad frente a poder interactuar con otros sistemas que no la pueden implementar.

 

Saludos a todos…

POCO en Entity Framework 4.0

La próxima versión de Entity Framework 4.0 permitirá la opción de trabajar con nuestras clases POCO. Ahora podremos desarrollar nuestras aplicaciones N-tiers reutilizando nuestros objetos en cualquiera de las capas, ya que estos serán objetos planos sin referencias algunas a Entity Framework.

Vamos a ver un pequeño ejemplo de cómo sería crear un modelo a partir de nuestras clases POCO, que podrían ser como estas:

EF_POCO1

Este modelo se ha generado con el diseñador de entidades (bien utilizando el método Model-First o el método Generar desde la base de datos). Las clases para estas entidades serían las siguientes:

   1: public class Customer

   2: {

   3:     public int Id { get; set; }

   4:     public String Name { get; set; }

   5:     public String Address { get; set; }

   6:     public IList<Contact> Contacts { get; set; }

   7: }

   1: public class Contact

   2: {

   3:     public int Id { get; set; }

   4:     public String Name { get; set; }

   5:     public String PhoneNumber { get; set; }

   6:     public Customer Customer { get; set; }

   7: }

Si vemos las propiedades de nuestro fichero edm (no de nuestro modelo), tenemos una nueva propiedad que nos permite especificar la herramienta que vamos a utilizar para generar nuestro código, por defecto utiliza EntityModelCodeGenerator.

EF_POCO2

Para poder utilizar nuestras propias clases POCO, tenemos que dejar vació el Custom Tool, para que no genere ningún código con el ObjectContext de Entity Framework.

Al especificar que no queremos que nos genere un ObjectContext, tendremos que crearlo nosotros, definiendo todas las colecciones que queremos utilizar en nuestras consultas.

Un ObjectContext podría ser el siguiente:

   1: public class ManagerEntities : ObjectContext

   2: {

   3:     private ObjectSet<nTierTest.Model.Contact> _contacts;

   4:     private ObjectSet<nTierTest.Model.Customer> _customers;

   5:  

   6:     public ManagerEntities()

   7:         : base("Name=ManagerEntities")

   8:     {

   9:         DefaultContainerName = "ManagerEntities";

  10:         ContextOptions.LazyLoadingEnabled = true;

  11:         _contacts = CreateObjectSet<nTierTest.Model.Contact>("ManagerEntities.Contact");

  12:         _customers = CreateObjectSet<nTierTest.Model.Customer>("ManagerEntities.Customer");

  13:     }

  14:  

  15:     public ObjectSet<nTierTest.Model.Contact> Contacts

  16:     {

  17:         get { return _contacts; }

  18:     }

  19:  

  20:     public ObjectSet<nTierTest.Model.Customer> Customers

  21:     {

  22:         get { return _customers; }

  23:     }

  24: }

Los nombres de las entidades las podemos ver en nuestro modelo, incluyendo el DefaultContainerName que lo tenemos en la propiedad Entity Container Name.

Con este contexto, podemos realizar consultas sobre nuestras entidades y nos devolveran colecciones de nuestras clases POCO.

   1: var context = new nTierTest.Data.ManagerEntities();

   2:  

   3: List<nTierTest.Model.Customer> customerList = (from c in context.Customers

   4:                                                select c).ToList();

   5:  

   6: var contacts = from c in context.Contacts

   7:                where c.Customer.Id == 1

   8:                select c;

Lo siguiente sería crear los métodos necesarios para añadir entidades al contexto, validadores para nuestras clases, etc.

   1: public void AddCustomer(nTierTest.Model.Customer customer)

   2: {

   3:     _customers.AddObject(customer);

   4: }

Aunque este es un ejemplo muy simple de como configurar POCO con Entity Framework, nos da una idea de las nuevas opciones que se nos abren para el desarrollo de nuestras aplicaciones.

 

Saludos a todos…

Más rápido con Visual Studio 2010. Zoom y Búsqueda incremental

Zoom

El equipo de desarrollo de Visual Studio 2010, intentando mejorar la experiencia del usuario (este término que tan de moda se ha puesto), nos permiten, en el nuevo IDE desarrollado con WPF, la posibilidad de hacer Zoom en los editores de código.

Para esta acción, tenemos varias posibilidades:

Seleccionar el nivel de Zoom en el indicador de la ventana activa.

RapidoVS2010-Zoom1

Utilizar el ratón (Ctrl+Mouse Wheel)

Para esto sólo tenemos que mantener pulsado la tecla Ctrl y girar la rueda del ratón para hacer Zoom-In o Zoom-Out, igual que si lo hiciéramos en Internet Explorer o en Word.

Utilizar los Keyboard Shorcut para hacer zoom.

Zoom-In Ctrl+Shift+punto
Zoom-Out Ctrl+Shift+coma

Búsqueda incremental

La búsqueda incremental se utiliza para localizar un texto de manera rápida en el documento que tenemos abierto. Es muy eficaz porque sólo tenemos que escribir lo que queremos buscar, sin necesidad de interactuar con el IDE.

Cuando pulsamos las teclas Ctrl+i, se activa esta búsqueda y podemos empezar a escribir el término que queremos localizar.

Según vamos escribiendo, podremos ver que el cursos se ha posicionado en la primera ocurrencia de la búsqueda y la ha remarcado para que la localicemos mejor.

RapidoVS2010-Incremental1

Podemos ver el termino que está buscando en la barra de progreso del IDE.

RapidoVS2010-Incremental2

Cada vez que pulsemos las teclas Ctrl+i, el cursor se posicionará en la siguiente ocurrencia, y así sucesivamente. Para posicionarnos en la ocurrencia anterior, sólo tendremos que pulsar Ctrl+Shift+I, si queremos borrar un caracter del texto buscado pulsamos la tecla Backspace y cuando queramos finalizar la búsqueda solo tendremos que pulsar ESC.

Y hasta aquí dos características, una de ellas nuevas, de Visual Studio 2010 que nos ayudarán en nuestro día a día.

 

Saludos a todos…

Más rápido con Visual Studio 2010. Intellisense para TDD

En Visual Studio 2010 tenemos dos modos de Intellisense, Suggestion y Standard Completion. El modo Completion se usa cuando nuestras clases, métodos o miembros están definidos, Suggestion es el llamado ”TDD Friendly” y ahora veremos por qué.

RapidoVS2010-Intellisense1

Cuando escribimos código con el modo Completion activado, intellisense nos muestra la lista de miembros existentes pertenecientes a la búsqueda de lo que estamos escribiendo. Si aceptamos la selección, pulsando alguna de las teclas predefinidas para ello (Tabulados, punto y coma o enter), se quedará escrito el elemento de la lista que esté seleccionado en ese momento.

RapidoVS2010-Intellisense2

 

 

Sin embargo, cuando escribimos con el modo Suggestion, activándolo con la combinacion Ctrl-Alt-Space, el comportamiento de intellisense es distinto. Aunque nos muestra la lista de miembros existentes que estamos buscando, a esta lista se ha añadido un cuadro de texto que va escribiendo lo que nosotros hemos escrito y no tenemos seleccionado ningún elemento de la lista. Esto nos permite elegir si queremos un elemento de la lista o si queremos el texto que hemos escrito y que nos lo previsualiza en el cuadro de texto.

RapidoVS2010-Intellisense3

Este pequeño cambio, nos ayudará cuando estemos con TDD o escribiendo métodos que no existen y los crearemos más adelante.

 

Saludos a todos…

Más rápido con Visual Studio 2010. Reemplazo multi-línea

Empezamos con una serie de artículos para escribir código mejor y más rápido con Visual Studio 2010.

Visual Studio 2010 nos permite seleccionar un bloque de texto presionando la tecla ALT.

RapidoVS2010_2D00_Reemplazo1[1]

Una que lo tenemos seleccionado, podemos escribir para reemplazar el texto seleccionado en todas las líneas a la vez.

RapidoVS2010_2D00_Reemplazo2[1]

Esto nos permite hacer cambios en nuestro código rápido y en varias líneas simultáneamente.

 

Saludos a todos…

Transacción no permitida en Entity Framework

Actualizado. El problema no era la transacción del contexto de Entity Framework (Entity Framework no hace uso de transacciones para la consulta de datos si no se lo especificamos, aunque si para las actualizaciones (insert, update o delete)). El problema reside en que recorriendo la colección hay un Reader abierto y no permite la modificación de los datos del mismo. Gracias a Unai por las aclaraciones con las transacciones y el Reader en Entity Framework.

El problema nos surge cuando obtenemos una colección de entidades, las recorremos con un ForEach y realizamos alguna actualización de estas entidades o de otras dentro de este ForEach, realizando un SaveChanges para cada una de las entidades de la colección que estamos recorriendo.

   1: using (var context = new ClientesModelContainer())

   2: {

   3:     var empresaList = from p in context.EmpresaSet

   4:                       select p;

   5:  

   6:     foreach (var empresa in empresaList)

   7:     {

   8:         empresa.Direccion.Calle = "Actualizacion Calle 1";

   9:         empresa.Nombre = "Nombre Empresa 1";

  10:  

  11:         Vehiculo vehiculo = new Vehiculo();

  12:         vehiculo.Marca = "Marca 1";

  13:         vehiculo.Modelo = "Modelo 1";

  14:  

  15:         context.AddToVehiculoSet(vehiculo);

  16:  

  17:         context.SaveChanges();

  18:         Console.WriteLine("Actualizada {0}", empresa.Nombre);

  19:     }

  20:  

  21:     Console.ReadLine();

  22: }

El resultado de esta actualización es un EntityException que nos indica que se ha producido un error iniciando una transacción en el proveedor de conexión y con el siguiente mensaje de exepción ‘New transaction is not allowed because there are other threads running in the session’ (No están permitidas nuevas transacciones cuando existen otros hilos ejecutándose en la sesión).

Esto nos indica que cuando Entity Framework ejecuta el SaveChanges e intenta ejecutar actualizaciones sobre la conexión que está abierta por el ForEach, el proveedor SqlClient no se lo permite porque existe un Reader abierto y no es posible la actualización del mismo.

Veamos las posibles soluciones a este problemilla.

1. Ejecutar el SaveChanges fuera del ForEach y al final de todas las modificaciones. Esta solución nos pueda valer, siempre que nuestro código no necesite que se actualice la base de datos y se pueda esperar al final para realizar los cambios, el código quedaría tal como sigue.

   1: using (var context = new ClientesModelContainer())

   2: {

   3:    var empresaList = from p in context.EmpresaSet

   4:                      select p;

   5:  

   6:    foreach (var empresa in empresaList)

   7:    {

   8:        empresa.Direccion.Calle = "Actualizacion Calle 1";

   9:        empresa.Nombre = "Nombre Empresa 1";

  10:  

  11:        Vehiculo vehiculo = new Vehiculo();

  12:        vehiculo.Marca = "Marca 1";

  13:        vehiculo.Modelo = "Modelo 1";

  14:        context.AddToVehiculoSet(vehiculo);

  15:  

  16:        Console.WriteLine("Actualizada {0}", empresa.Nombre);

  17:    }

  18:  

  19:    context.SaveChanges();

  20:  

  21:    Console.ReadLine();

2. Obtener la colección antes de recorrerla. Este es el método que prefiero, ya que obtenemos la colección de entidades en un Array o List para que no se mantenga abierto el Reader.

   1: using (var context = new ClientesModelContainer())

   2: {

   3:     var empresaList = (from p in context.EmpresaSet

   4:                       select p).ToList();

   5:  

   6:     foreach (var empresa in empresaList)

   7:     {

   8:         empresa.Direccion.Calle = "Actualizacion Calle 1";

   9:         empresa.Nombre = "Nombre Empresa 1";

  10:  

  11:         Vehiculo vehiculo = new Vehiculo();

  12:         vehiculo.Marca = "Marca 1";

  13:         vehiculo.Modelo = "Modelo 1";

  14:  

  15:         context.AddToVehiculoSet(vehiculo);

  16:  

  17:         context.SaveChanges();

  18:         Console.WriteLine("Actualizada {0}", empresa.Nombre);

  19:     }

  20:  

  21:     Console.ReadLine();

  22: }

3. Especificar la Transacción. Podemos especificar, haciendo uso del TransactionScope, la transacción de la conexión para delegar en este la actualización de los datos.

   1: using (TransactionScope transactionScope = new TransactionScope())

   2: {

   3:     using (var context = new ClientesModelContainer())

   4:     {

   5:         var empresaList = from p in context.EmpresaSet

   6:                           select p;

   7:  

   8:         foreach (var empresa in empresaList)

   9:         {

  10:             empresa.Direccion.Calle = "Actualizacion Calle 1";

  11:             empresa.Nombre = "Nombre Empresa 1";

  12:  

  13:             Vehiculo vehiculo = new Vehiculo();

  14:             vehiculo.Marca = "Marca 1";

  15:             vehiculo.Modelo = "Modelo 1";

  16:  

  17:             context.AddToVehiculoSet(vehiculo);

  18:  

  19:             context.SaveChanges();

  20:             Console.WriteLine("Actualizada {0}", empresa.Nombre);

  21:         }

  22:  

  23:         Console.ReadLine();

  24:     }

  25: }

 

Este código no suele ser el más empleado, ya que las aplicaciones que desarrollamos con Entity Framework se preparan para que soporten la desconexión de los contextos y de las entidades (n-tiers), pero puede convertirse en un dolor de cabeza, si recibimos este tipo de error que no es muy descriptivo.

 

Saludos a todos…

Tipos Complejos en el EDM Designer de Visual Studio 2010

Una de las mejoras que se han incluido en el diseñador de entidades de Visual Studio 2010 para Entity Framework, es el soporte para crear y modificar los Complex Types (Tipos Complejos). Los Complex Types son una de las grandes características de Entity Framework, que si bien podíamos incluirlas en la versión EF1, si utilizábamos esta característica no podíamos acceder al modelo con el diseñador de Visual Studio 2008.

Visual Studio 2010 nos permite diseñar nuestro modelo EDM incluyendo estos tipos complejos, veamos un ejemplo.

Imaginemos que tenemos una entidad Empresa con las propiedades Calle, Numero, Planta, Localidad y Provincia. Queremos encapsular estas propiedades en una única propiedad compleja llamada Direccion.

Con el nuevo diseñador de Visual Studio 2010 podemos refactorizar estas propiedades y convertirlas en el tipo complejo que queramos.

ComplexType1[1]

ComplexType2[1]

El diseñador se encarga de enlazar las nuevas propiedades a su correspondiente campo en la tabla. La propiedad Direccion.Calle se enlaza con el campo Calle en la tabla y así sucesivamente con las demás propiedades que conforman el Complex Type.

 ComplexType3[1]

Cuando actualizamos una de las propiedades de un Complex Type, Entity Framework actualiza todas las propiedades del Complex Type y no sólo la que hemos cambiado. Por ejemplo:

   1: using (var context = new ClientesModelContainer())

   2: {

   3:     var empresa1 = (from p in context.EmpresaSet

   4:                    where p.Id == 1

   5:                    select p).FirstOrDefault();

   6:  

   7:     empresa1.Direccion.Calle = "Actualizacion Calle 1";

   8:     

   9:     context.SaveChanges();

  10: }

y revisamos la consulta que se ejecuta en el SaveChanges, podemos ver que se actualizan todos los campos del Complex Type.

   1: update [dbo].[EmpresaSet]

   2: set [Direccion_Calle] = @0, 

   3:     [Direccion_Numero] = @1, 

   4:     [Direccion_Planta] = @2, 

   5:     [Direccion_Localidad] = @3, 

   6:     [Direccion_Provincia] = @4

   7: where ([Id] = @5)

Por el contrario, si actualizamos cualquiera de las otras propiedades que no pertenecen al Complex Type,

   1: empresa1.Nombre = "Nombre de Empresa 1";

   2:  

   3: context.SaveChanges();

el resultado es una consulta SQL con los campos que se quieren actualizar.

   1: update [dbo].[EmpresaSet]

   2: set [Nombre] = @0

   3: where ([Id] = @1)

Importante tener en consideración el comportamiento de las consultas que se ejecutan al actualizar una entidad con un Complex Type, aunque no por esta peculiaridad debemos de dejar de modelar nuestras entidades con estos tipos complejos. Gracias a ellos ganamos en simplicidad en nuestro modelo.

Saludos a todos…

Visual Studio 2010 Setup Project Prerequisitos

Cuando creamos un proyecto de Setup con Visual Studio 2010 podemos seleccionar los componente que se van a instalar (si no lo están ya), los Prerequisitos. Una vez creado el proyecto de Setup, nos vamos a las propiedades del mismo y en la opción de Prerequisites tenemos los componentes que deben de existir en el sistema para que se pueda instalar y ejecutar nuestra aplicación.

SetupPrerequisites1[1]

En la ventana de Prerequisitos podemos elegir la versión del Framework, los Interops de Office, SQL Server Express, Runtime de C++, etc, y de donde lo debe de descargar si no lo tuviera instalado.

SetupPrerequisites2[1]

Si queremos crear un Setup para una aplicación que necesita Framework 3.5 SP1, debemos tener en cuenta que no sólo tendremos con marcar esta opción en la ventana anterior. Si desmarcamos el Framework 4 Client Profile que nos viene por defecto y marcamos el Framework 3.5 SP1 que es el que necesitamos, al generar el setup e instalar en el cliente nos vamos a encontrar con el problema de que va a instalar el Framework 4.0, aunque nosotros no se lo hayamos pedido y aunque nuestra aplicación sólo necesite el Framework 3.5.

Este problema sucede aunque seleccionemos el Framework 3.5 en la ventana de creación de proyecto.

SetupPrerequisites_2D00_NewProject[1]

El problema reside en las condiciones de ejecución del setup (botón derecho en el proyecto de setup, menú View y seleccionamos Launch Conditions).

En esta ventana, tenemos las condiciones de requerimientos para ejecutar el setup y por defecto tenemos una condición que comprueba que el Framework esté instalado.

SetupPrerequisitesLaunchCondition[1]

Si suponemos que hemos creado un Setup Project para Framework 3.5 SP1, ¿por qué en las propiedades de la condición comprueba que esté instalado el .NET Framework 4? Pues tendremos que cambiar también esta opción para que nuestro setup no compruebe ni instale el Framework 4, cuando queremos instalar una aplicación en Framework 3.5

Voy a enviar el posible bug a Microsoft a ver que dicen sobre él.

Saludos a todos…

Evento Developer & Design Tour Tenerife. Productividad con Visual Studio 2010

El 27 de Noviembre Microsoft presentó en Tenerife Visual Studio 2010, Windows Azure y Expression Blend 3. Este post es para publicar la presentación que realicé sobre Productividad con Visual Studio 2010 (que ya me la han pedido varias veces).

Quiero agradecer a Microsoft por permitirnos dar esta sesión y sobre a Luis Fraile por darme las guías de la presentación.

Con Visual Studio 2010 nos viene un nuevo IDE basado en WPF con muchas y grandes mejoras y como objetivo principal mejorar nuestra productividad. Nos vienen opciones de Multi-Monitor, mejoras para desarrollar con TDD, nuevas funcionalidades de navegación por el código, zoom (si, tendremos zoom con la ruedita del ratón).

Otro de los grandes cambios es a nivel de Intellisense que es capaz de realizar búsquedas en el texto y en los snippets, que han sido ampliados y tenemos snippets hasta de Javascript para poder hacer un surround con un try de nuestro código.

A nivel de despliegue de aplicaciones web se han añadido los Transformers, un motor de transformación XML que nos permite tener diversos Web.Config en función del entorno de nuestros servidores de despliegue y poder hacer paquetes con esta configuración personalizada.

En la depuración el gran héroe es el Intellitrace, un histórico de depuración que nos permite capturar la pila completa de ejecución de la aplicación y de sus llamadas y eventos.

Y mucho más que ya iremos viendo como novedades en el Framework 4.0, Dynamic Language Runtime, PLINQ y otros.

Cualquier duda, pregunta o sugerencia sobre Visual Studio 2010 me la podéis hacer sin problema.

Presentación Productividad con Visual Studio 2010