Aplicaciones de N niveles y Entity Framework

Creación de aplicaciones de N niveles con EF4

Daniel Simmons

Descargar el ejemplo de código

Este es el tercer artículo de una serie acerca de la programación de N niveles con Entity Framework (consulte msdn.microsoft.com/magazine/dd882522.aspx y msdn.microsoft.com/magazine/ee321569.aspx), en especial sobre la creación de servicios web personalizados con Entity Framework (EF) y Windows Communication Foundation (WCF). (En algunas situaciones, es adecuado un servicio basado en REST o algún otro enfoque, pero en estos artículos me he centrado en los servicios web personalizados). El primer artículo describió un número de importantes antipatrones y consideraciones del diseño. En el segundo artículo escribí acerca de cuatro patrones que se pueden usar correctamente en una aplicación de N niveles. Ese artículo también incluyó ejemplos de código que ilustran cómo se puede usar la primera versión de Entity Framework (EF 3.5 SP1) para implementar lo que llamo el patrón Entidades simples. En este artículo examinaremos algunas características que aparecen en la segunda versión de Entity Framework (EF4) y la manera de usarlas para implementar los patrones de N niveles Entidades de seguimiento automático y Objetos de transferencia de datos (DTO).

A pesar de que Entidades simples normalmente no es el patrón preferido para las aplicaciones de N niveles, es la opción más viable en la primera versión de EF. Sin embargo, EF4 cambia significativamente las opciones para la programación de N niveles con el marco. Algunas de las nuevas características clave incluyen lo siguiente:

  1. Nuevos métodos de marco que admiten operaciones desconectadas, como ChangeObjectState y ChangeRelationshipState, que cambian una entidad o una relación a un estado nuevo (por ejemplo, agregado o modificado); ApplyOriginalValues, que permite definir los valores originales para una entidad y el nuevo evento ObjectMaterialized que se activa cada vez que el marco crea una entidad.
  2. Compatibilidad con objetos CLR tradicionales (POCO) y valores de clave externa en las entidades. Estas características le permiten crear clases de entidad que se pueden compartir entre la implementación del servicio de nivel intermedio y otros niveles, que probablemente no tengan la misma versión de Entity Framework (.NET 2.0 o Silverlight, por ejemplo). Los objetos POCO con claves externas tienen un formato de serialización sencillo que simplifica la interoperabilidad con plataformas como Java. El uso de claves externas también permite un modelo de concurrencia mucho más simple para las relaciones.
  3. Plantillas T4 para personalizar la generación de código. Estas plantillas proporcionan una manera de generar clases que implementan los patrones DTO o Entidades de seguimiento automático.

El equipo de Entity Framework ha usado estas características para implementar el patrón Entidades de seguimiento automático en una plantilla, con lo que ese patrón es mucho más accesible, y a pesar de que los DTO siguen necesitando la mayor cantidad de trabajo durante la implementación inicial, este proceso también es más fácil con EF4. (La plantilla Entidades de seguimiento automático y algunas otras características de EF están disponibles como parte de una característica de descarga web de Community Technology Preview (CTP) en lugar de estarlo en la caja de Visual Studio 2010/.NET 4. Los ejemplos que aparecen en este artículo suponen que tanto Visual Studio 2010/.NET 4 como la característica CTP están instalados). Con estas nuevas capacidades, una forma de evaluar los cuatro patrones que he descrito (Entidades simples, Conjunto de cambios, Entidades de seguimiento automático y DTO) es en términos de un equilibrio entre el valor de la arquitectura (separación de preocupaciones/acoplamiento flexible, resistencia del contrato, formato eficiente de la conexión e interoperabilidad) y la facilidad de la implementación y el tiempo de comercialización. Si traza los cuatro patrones en un gráfico que represente este equilibrio, es probable que el resultado se vea similar a la figura 1.


Figura 1 Comparación de los patrones de N niveles con EF4

El patrón correcto para una situación específica depende de muchos factores. En general, los DTO proporcionan muchas ventajas de arquitectura con un alto costo inicial de implementación. Conjunto de cambios exhibe pocas características de arquitectura buenas, pero es fácil de implementar (cuando está disponible para una tecnología específica, por ejemplo, el DataSet en el ADO.NET tradicional).

Recomiendo un equilibrio pragmático y ágil entre estas preocupaciones al empezar con Entidades de seguimiento automático y avanzando a los DTO si la situación así lo garantiza. A menudo puede levantarse y ponerse en ejecución rápidamente con Entidades de seguimiento automático y seguir logrando muchos importantes objetivos de arquitectura. Este enfoque representa un equilibrio mucho mejor que Conjunto de cambios o Entidades simples, las que sólo recomendaría si no hubiese otras opciones viables. Por otro lado, los DTO son definitivamente la mejor opción cuando la aplicación se vuelve cada vez mayor y más compleja, o en caso de que tenga requisitos que Entidades de seguimiento automático no pueda satisfacer, como tasas distintas de cambio entre el cliente y el servidor. Estos dos patrones son las herramientas más importantes que tiene en su cuadro de herramientas, por lo que vamos a darles un vistazo.

Entidades de seguimiento automático

Para usar este patrón con Entity Framework, comience por crear un modelo de datos de entidades que represente las entidades conceptuales y asígnelo a una base de datos. Puede aplicar ingeniería inversa a un modelo proveniente de una base de datos que tenga y personalizarlo, o puede crear un modelo desde cero y luego generar una base de datos para hacerlos coincidir (otra característica nueva en EF4). Una vez establecido este modelo y la asignación, reemplace la plantilla de generación de código predeterminada con la plantilla Entidades de seguimiento automático con un clic del botón secundario en la superficie del diseñador de entidades y eligiendo Agregar elemento de generación de código.

A continuación, elija la plantilla Entidades de seguimiento automático desde la lista de plantillas instaladas. Este paso desactiva la generación de código predeterminada y agrega dos plantillas al proyecto: una plantilla genera ObjectContext y la otra genera clases de entidad. Separar la generación de código en dos plantillas hace posible dividir el código en ensamblados independientes, uno para las clases de entidad y otro para el contexto.

La ventaja principal que tiene este enfoque es que puede tener sus clases de entidad en un ensamblado que no tenga dependencias en Entity Framework. De este modo y si lo desea, el nivel intermedio y el cliente pueden compartir el ensamblado de entidad (o al menos el código que genera) y cualquier lógica de negocios que haya implementado. El contexto se mantiene en un ensamblado que tiene dependencias tanto en las entidades como en EF. Si el cliente del servicio está ejecutando .NET 4, simplemente puede hacer referencia al ensamblado de entidad desde el proyecto del cliente. Si el cliente ejecuta una versión anterior de .NET o Silverlight, es probable que desee agregar vínculos desde el proyecto del cliente a los archivos generados y volver a compilar el origen de la entidad en ese proyecto (teniendo como objetivo el CLR adecuado).

Independiente de la manera en que estructura el proyecto, las dos plantillas trabajan en conjunto para implementar el patrón Entidades de seguimiento automático. Las clases de entidad generadas son simples clases de POCO cuya única característica que va más allá del almacenamiento básico de propiedades de entidad es llevar un registro de los cambios en las entidades, el estado general de una entidad, los cambios en propiedades críticas como tokens de concurrencia y cambios en las relaciones entre las entidades. Esta información de seguimiento adicional es parte de la definición de DataContract para las entidades (para que cuando envía una entidad desde o hasta un servicio WCF, se transporte la información de seguimiento).

En el cliente del servicio, se realiza un seguimiento automático de los cambios en las entidades incluso si las entidades no están conectadas a ningún contexto. Cada entidad generada tiene código similar al siguiente para cada propiedad. Si cambia el valor de una propiedad en una entidad que tenga, por ejemplo, el estado Sin modificar, el estado cambia a Modificado:

[DataMember]
public string ContactName
{
    get { return _contactName; }
    set
    {
            if (!Equals(_contactName, value))
            {
                _contactName = value;
                OnPropertyChanged("ContactName");
            }
    }
}
private string _contactName;

De manera similar, si las entidades nuevas se agregan a un gráfico o se eliminan entidades de un gráfico, se registra esa información. Debido a que el estado de cada entidad se registra en la misma entidad, el mecanismo de seguimiento se comporta como se podría esperar, incluso cuando relaciona entidades recuperadas desde más de una llamada de servicio. Si establece una nueva relación, sólo se registra ese cambio; la entidad implicada mantiene el mismo estado, como si todas se hubiesen recuperado desde una sola llamada de servicio.

La plantilla de contexto agrega un método nuevo, ApplyChanges, al contexto generado. ApplyChanges adjunta un gráfico de entidades al contexto y configura la información en el ObjectStateManager para coincidir con la información registrada en las entidades. Con la información que las entidades registran sobre ellas mismas y ApplyChanges, el código generado administra tanto las preocupaciones de seguimiento de cambios como las de concurrencia, dos de las partes más difíciles de implementar correctamente una solución de N niveles.

Como ejemplo concreto, la figura 2 muestra un ServiceContract simple que podría usar con Entidades de seguimiento automático para crear un sistema de envío de pedidos de N niveles basado en la base de datos de ejemplo de Northwind.

Figura 2 Un contrato de servicio simple para el patrón Entidades de seguimiento automático

[ServiceContract]
public interface INorthwindSTEService
{
    [OperationContract]
    IEnumerable<Product> GetProducts();

    [OperationContract]
    Customer GetCustomer(string id);

    [OperationContract]
    bool SubmitOrder(Order order);

    [OperationContract]
    bool UpdateProduct(Product product);
}

El método de servicio GetProducts se usa para recuperar datos de referencia en el cliente acerca del catálogo de productos. Normalmente esta información se almacena en la memoria caché de forma local y no se actualiza a menudo en el cliente. GetCustomer recupera un cliente y una lista de los pedidos de ese cliente. La implementación de ese método es muy simple, como aquí aparece:

public Customer GetCustomer(string id)
{
    using (var ctx = new NorthwindEntities())
    {
        return ctx.Customers.Include("Orders")
        .Where(c => c.CustomerID == id)
        .SingleOrDefault();
    }
}

Este es esencialmente el mismo código que escribiría para una implementación de este tipo de método con el patrón Entidades simples. La diferencia es que las entidades que se devuelven son de seguimiento automático, lo que significa que el código de cliente para usar estos métodos también es muy simple, pero puede lograr mucho más.

Para ilustrarlo, supongamos que en el proceso de envío de pedidos no sólo desea crear un pedido con líneas de detalles de pedidos adecuadas, sino que también actualizar partes de la entidad de cliente con la información de contacto más reciente. Además, desea eliminar cualquier pedido que tenga una OrderDate nula (quizás el sistema marca como rechazados los pedidos de esa manera). Con el patrón Entidades simples, la combinación de agregar, modificar y eliminar entidades en un solo gráfico requeriría varias llamadas de servicio para cada tipo de operación o una implementación de servicio y contrato personalizado muy complicada si intentase implementar algo similar a Entidades de seguimiento automático en la primera versión de EF. Con EF4, el código de cliente podría verse como la figura 3.

Figura 3 Código de cliente para el patrón Entidades de seguimiento automático

var svc = new ChannelFactory<INorthwindSTEService>(
    "INorthwindSTEService")
    .CreateChannel();

var products = new List<Product>(svc.GetProducts());
var customer = svc.GetCustomer("ALFKI");

customer.ContactName = "Bill Gates";

foreach (var order in customer.Orders
    .Where(o => o.OrderDate == null).ToList())
{
    customer.Orders.Remove(order);
}

var newOrder = new Order();
newOrder.Order_Details.Add(new Order_Detail()
    {
        ProductID = products.Where(p => p.ProductName == "Chai")
                    .Single().ProductID,
        Quantity = 1
    });
customer.Orders.Add(newOrder);

var success = svc.SubmitOrder(newOrder);

Este código crea el servicio, llama a los dos primeros métodos en él para obtener la lista de productos y una entidad de cliente y luego realiza cambios en el gráfico de la entidad de cliente usando el mismo tipo de código que podría escribir si estuviese creando una aplicación de Entity Framework de dos niveles que se comunique directamente con la base de datos o si estuviese implementando un servicio en el nivel intermedio. (Si no está familiarizado con este estilo de creación de un cliente de servicio WCF, crea automáticamente un proxy de cliente sin crear servidores proxy para las entidades, porque estamos usando nuevamente las clases de entidad desde la plantilla Entidades de seguimiento automático. También podría usar el cliente generado por el comando Agregar referencia de servicio en Visual Studio si lo desea). Pero aquí no hay ObjectContext implicado. Sólo está manipulando las entidades. Finalmente, el cliente llama al método de servicio SubmitOrder para hacer subir los cambios al nivel intermedio. 

Por supuesto, en una aplicación real los cambios que el cliente hace en el gráfico probablemente vendrían desde una interfaz de usuario de algún tipo y agregaría control de excepciones en torno a las llamadas de servicio (es importante especialmente cuando tiene que comunicarse a través de la red), pero el código que aparece en la figura 3 ilustra los principios. Otro elemento importante que hay que observar es que cuando crea la entidad de detalle de pedido para el pedido nuevo, configura sólo la propiedad ProductID en lugar de configurar la entidad Product misma. Esta es la nueva característica de relación de clave externa en acción. Reduce la cantidad de información que viaja a través de la transmisión, porque sólo serializa el ProductID de vuelta al nivel intermedio y no una copia de la entidad de producto.

Es en la implementación del método de servicio SubmitOrder donde realmente se destaca Entidades de seguimiento automático:

public bool SubmitOrder(Order newOrder)
{
    using (var ctx = new NorthwindEntities())
    {
        ctx.Orders.ApplyChanges(newOrder);
        ValidateNewOrderSubmission(ctx, newOrder);
        return ctx.SaveChanges() > 0;
    }
}

La llamada a ApplyChanges realiza toda la magia. Lee la información de cambios desde las entidades y la aplica al contexto de manera tal que el resultado sea el mismo que si esos cambios se hubiesen realizado todo el tiempo en entidades adjuntas al contexto.

Validación de los cambios

Otra cosa que debe tener en cuenta en la implementación de SubmitOrder es la llamada a ValidateNewOrderSubmission. Este método, que agregué a la implementación de servicio, examina el ObjectStateManager para asegurarse de que sólo están presentes los tipos de cambios que esperamos en una llamada a SubmitOrder.

Este paso es muy importante porque, por sí mismo, ApplyChanges empuja cualquier cambio que encuentra en un gráfico completo de objetos relacionados al contexto. Nuestra expectativa de que el cliente sólo agregará pedidos nuevos, actualizará al cliente, etc., no significa que un cliente con errores (o incluso malicioso) no haría nada más. ¿Qué pasa si cambiara el precio de un producto para hacer que un pedido sea más barato o más caro de lo que debiera ser? Los detalles sobre cómo se realiza la validación son menos importantes que la regla crítica que establece que siempre debe validar los cambios antes de guardarlos en la base de datos. Esta regla se aplica independiente del patrón de N niveles que use.

 Un segundo principio de diseño crítico es que debe desarrollar métodos de servicio específicos por separado para cada operación. Sin estas operaciones por separado, no tiene un contrato seguro que represente lo que se permite y lo que no entre los dos niveles y la validación adecuada de los cambios puede llegar a ser imposible. Si tenía un solo método de servicio SaveEntities en lugar de un método SubmitOrder y UpdateProduct por separado (al que sólo pueden obtener acceso usuarios autorizados para modificar el catálogo de productos), podía implementar fácilmente la parte de aplicar y guardar de ese método, pero no podría validar de manera adecuada, porque no tendría manera de saber cuándo se permiten las actualizaciones de productos y cuando no.

Objetos de transferencia de datos

El patrón Entidades de seguimiento automático hace que el proceso de N niveles sea fácil, y si crea métodos de servicio específicos y valida cada uno de ellos, puede ser bastante firme en términos de arquitectura. Incluso así, hay límites para lo que puede hacer con el patrón. Cuando se encuentra con esos límites, los DTO son la solución para el dilema.

En los DTO, en lugar de compartir una implementación de entidad única entre el nivel intermedio y el cliente, crea un objeto personalizado que sólo se usa para transferir datos a través del servicio y desarrollar implementaciones de entidad por separado para el nivel intermedio y el cliente. Este cambio brinda dos beneficios principales: aísla el contrato de servicio de los problemas de implementación en el nivel intermedio y el cliente, lo que permite que el contrato siga estable incluso si cambia la implementación en los niveles, y le permite controlar los datos que fluyen a través de la transmisión. Por lo tanto, puede impedir el envío de datos innecesarios (o de datos a los que el cliente no está autorizado a obtener acceso) o volver a dar forma a los datos para hacerlos más convenientes para el servicio. Generalmente, el contrato de servicio está diseñado teniendo en mente los escenarios de cliente para que sea posible volver a dar forma a los datos entre las entidades de nivel intermedio y los DTO (probablemente combinando varias entidades en un DTO y pasando por alto las propiedades que no se necesitan en el cliente), mientras que es posible usar directamente los DTO en el cliente.

Sin embargo, estos beneficios implican el costo de tener que crear y mantener una o dos capas adicionales de objetos y asignaciones. Para ampliar el ejemplo de envío de pedidos, podría crear una clase sólo con el fin de enviar pedidos nuevos. Esta clase combinaría propiedades de la entidad de cliente con propiedades del pedido que se están configurando en el nuevo escenario de pedidos, pero la clase dejaría fuera propiedades de ambas entidades que están calculadas en el nivel intermedio o configuradas en alguna otra etapa del proceso. Esto hace que el DTO sea lo más pequeño y eficiente posible. Es posible que la implementación tenga el siguiente aspecto:

public class NewOrderDTO
{
    public string CustomerID { get; set; }
    public string ContactName { get; set; }
    public byte[] CustomerVersion { get; set; }
    public List<NewOrderLine> Lines { get; set; }
}

public class NewOrderLine
{
    public int ProductID { get; set; }
    public short Quantity { get; set; }
}

Está bien, en realidad son dos clases, una para el pedido y otra para las líneas de detalles de pedidos, pero el tamaño de los datos se mantiene lo más pequeño posible. La única información aparentemente externa en el código es el campo CustomerVersion, que contiene la información de versión de la fila que se usa para las comprobaciones de concurrencia en la entidad de cliente. Necesita esta información para la entidad de cliente, porque la entidad ya existe en la base de datos. Para el pedido y las líneas de detalles, son entidades nuevas que se envían a la base de datos, por lo que no se necesita su información de versión y el OrderID; las genera la base de datos cuando los cambios son persistentes.

El método de servicio que acepta este DTO usa las mismas API de Entity Framework de nivel inferior que la plantilla Entidades de seguimiento automático usa para lograr sus tareas, pero ahora debe llamar a esas API directamente, en lugar de dejar que el código generado las llame. La implementación viene en dos partes. Primero, cree un gráfico de entidades de cliente, pedido y detalle de pedido según la información en el DTO (consulte la figura 4).

Figura 4 Creación de un gráfico de entidades

var customer = new Customer
    {
        CustomerID = newOrderDTO.CustomerID,
        ContactName = newOrderDTO.ContactName,
        Version = newOrderDTO.CustomerVersion,
    };

var order = new Order
    {
        Customer = customer,
    };

foreach (var line in newOrderDTO.Lines)
{
    order.Order_Details.Add(new Order_Detail
        {
            ProductID = line.ProductID,
            Quantity = line.Quantity,
        });
}

Luego adjunte el gráfico al contexto y configure la información de estado adecuada:

ctx.Customers.Attach(customer);
var customerEntry = ctx.ObjectStateManager.GetObjectStateEntry(customer);
customerEntry.SetModified();
customerEntry.SetModifiedProperty("ContactName");

ctx.ObjectStateManager.ChangeObjectState(order, EntityState.Added);
foreach (var order_detail in order.Order_Details)
{
    ctx.ObjectStateManager.ChangeObjectState(order_detail, 
       EntityState.Added);
}

return ctx.SaveChanges() > 0;

La primera línea adjunta todo el gráfico al contexto pero, cuando eso ocurre, cada entidad tiene el estado Sin modificar, por lo que primero indíquele al ObjectStateManager que cambie el estado de la entidad de cliente a Modificado, pero sólo con una propiedad, ContactName, marcada como modificada. Esto es importante porque en realidad no tiene toda la información de cliente, sino que sólo la información que estaba en el DTO. Si marcó todas las propiedades como modificadas, Entity Framework trataría de insistir en un buen número de valores nulos y ceros en otros campos de la entidad de cliente.

A continuación, cambie el estado del pedido y de cada uno de sus detalles de pedidos a Agregado y luego llame a SaveChanges.

¿Dónde está el código de validación? En este caso, como tiene un DTO muy específico para el escenario e interpreta ese objeto a medida que asigna la información desde él hacia sus entidades, realiza la validación a medida que avanza. No hay manera de que este código pudiera cambiar inadvertidamente el precio de un producto, porque nunca se toca la entidad de producto. Este es otro beneficio del patrón DTO, pero sólo de manera indirecta. Todavía tiene que realizar el trabajo de validación: el patrón sólo fuerza un nivel de validación. En muchos casos, el código necesita incluir la validación adicional de los valores u otras reglas empresariales.

Otra consideración es la administración adecuada de las excepciones de concurrencia. Como mencioné anteriormente, la información de versión para la entidad de cliente está incluida en el DTO, por lo que está configurado para detectar adecuadamente los problemas de concurrencia si alguien más modifica al mismo cliente. Un ejemplo más completo asignaría esta excepción a un error de WCF para que el cliente pudiera resolver el conflicto, o capturaría la excepción y aplicaría algún tipo de directiva automática para abordar el conflicto.

Si deseaba ampliar el ejemplo agregando otra operación, como la capacidad de modificar un pedido, podría crear otro DTO específicamente para ese escenario, con la información correcta justa para eso. Este objeto se vería similar a nuestro NewOrderDTO, pero tendría las propiedades OrderID y Version para las entidades de pedido y de detalles de pedido, así como también cada propiedad para la cual desea autorizar la autorización por parte de la llamada de servicio. La implementación del método de servicio también sería similar al método SubmitOrderDTO que mostramos anteriormente, revisando los datos de DTO, creando objetos de entidad correspondientes y luego definiendo su estado en el administrador de estados antes de guardar los cambios en la base de datos.

Si fuera a implementar el método de actualización de pedidos con Entidades de seguimiento automático y Objetos de transferencia de datos, encontraría que la implementación de Entidades de seguimiento automático vuelve a usar las entidades y comparte casi todo el mismo código de implementación de servicio con el nuevo método de envío de pedidos; la única diferencia sería el código de validación e incluso podría compartirse algo de eso. Sin embargo, la implementación de DTO requiere una clase de Objeto de transferencia de datos por separado para cada uno de los dos métodos de servicio y las implementaciones del método siguen patrones similares, pero tienen muy poco código, si lo hay, que se pueda compartir.

Sugerencias desde las trincheras

Presentamos algunas sugerencias sobre lo que se debe vigilar y comprender.

  • Asegúrese de volver a usar el código de entidad generado de la plantilla Entidad de seguimiento automático en el cliente. Si usa código proxy generado por Agregar referencia de servicio en Visual Studio o alguna otra herramienta, las cosas se ven correctas en su mayor parte, pero descubrirá que las entidades no realizan un seguimiento real de sus cambios en el cliente.
  • Cree una nueva instancia de ObjectContext en una instrucción Using para cada método de servicio a fin de eliminarlo antes de que el método se devuelva. Este paso es crítico para la escalabilidad del servicio. Asegura que las conexiones de base de datos no se mantienen abiertas a través de las llamadas de servicio y que el estado temporal usado por una operación en especial es basura que se recoge cuando se termina la operación. Entity Framework automáticamente almacena en caché los metadatos y otra información que necesita en el dominio de la aplicación, y las conexiones de base de datos de grupos ADO.NET, por lo que volver a crear el contexto cada vez es una operación rápida.
  • Use la nueva característica de relaciones de clave externa cada vez que sea posible. Hace que el cambio de relaciones entre las entidades sea mucho más fácil. Con asociaciones independientes (el único tipo de relación disponible en la primera versión de Entity Framework), las comprobaciones de concurrencia se realizan en las relaciones independiente de las comprobaciones de concurrencia realizadas en las entidades, y no hay forma de excluirse de estas comprobaciones de concurrencia de relaciones. El resultado es que los servicios deben llevar los valores originales de las relaciones y configurarlos en el contexto antes de cambiar las relaciones. Sin embargo, con relaciones de clave externa, la relación es simplemente una propiedad de la entidad, y si la entidad pasa su comprobación de concurrencia, no se necesita una comprobación adicional. Puede cambiar una relación si simplemente cambia el valor de clave externa.
  • **Tenga cuidado con las colisiones de EntityKey cuando adjunte un gráfico a un ObjectContext. **Por ejemplo, si usa DTO y partes del gráfico representan entidades agregadas recientemente para las que los valores clave de entidad no estén definidos, porque se generarán en la base de datos, debe llamar al método AddObject para agregar el gráfico completo de entidades y luego cambiar las entidades que no tengan estado Agregado al estado deseado, en lugar de llamar al método Attach y luego cambiando las entidades Agregadas a ese estado. De lo contrario, cuando llama por primera vez a Attach, Entity Framework piensa que cada entidad debe tener el estado Sin modificar, lo que supone que los valores clave de entidad son finales. Si más de una entidad de un tipo en especial tiene el mismo valor clave (por ejemplo, 0), Entity Framework arrojará una excepción. Si comienza con una entidad con estado Agregada, evita este problema porque el marco no espera que las entidades Agregadas tengan valores clave únicos.
  • Desactive la carga diferida automática (otra característica nueva de EF4) cuando se devuelvan entidades desde los métodos de servicio. Si no lo hace, el serializador activará la carga diferida y tratará de recuperar entidades adicionales desde la base de datos, lo que hará que se devuelvan más datos de los deseados (si las entidades están completamente conectadas, podría serializar toda la base de datos) o, lo que es más probable, recibirá un error porque se eliminará el contexto antes de que el serializador trate de recuperar los datos. Entidades de seguimiento automático no tiene activada la carga diferida de manera predeterminada, pero si crea una solución DTO es algo que tiene que vigilar.

Y al final

La versión .NET 4 de Entity Framework hace que la creación de aplicaciones de N niveles sólidas en su arquitectura sea mucho más fácil. Para la mayoría de las aplicaciones, recomiendo comenzar con la plantilla Entidades de seguimiento automático, que simplifica el proceso y permite volver a usar la mayor parte. Si tiene diferentes tasas de cambio entre el servicio y el cliente o si necesita tener el control absoluto del formato de la conexión, debe moverse a una implementación de Objetos de transferencia de datos. Independiente del patrón que elija, siempre tenga en cuenta los principios clave que representan los patrones y antipatrones y nunca olvide validar los datos antes de guardar.

Daniel Simmons es arquitecto miembro del equipo de Entity Framework en Microsoft.