Marco de entidades y agrupación de conexiones

268

Recientemente comencé a usar Entity Framework 4.0 en mi aplicación .NET 4.0 y tengo curiosidad acerca de algunas cosas relacionadas con la agrupación.

  1. La agrupación de conexiones, como sé, es administrada por el proveedor de datos ADO.NET, en mi caso el del servidor MS SQL. ¿Se aplica esto cuando crea una instancia de un nuevo contexto de entidades ( ObjectContext), es decir, el sin parámetros new MyDatabaseModelEntities()?

  2. ¿Cuáles son las ventajas y desventajas de a) crear un contexto de entidades globales para la aplicación (es decir, una instancia estática) ob) crear y exponer un contexto de entidades para cada operación / método dado, con un usingbloque.

  3. ¿Alguna otra recomendación, mejores prácticas o enfoques comunes para ciertos escenarios que debería conocer?

Noldorin
fuente

Respuestas:

369
  1. La agrupación de conexiones se maneja como en cualquier otra aplicación ADO.NET. La conexión de entidad todavía usa la conexión de base de datos tradicional con la cadena de conexión tradicional. Creo que puede desactivar la agrupación de conexiones en la cadena de conexión si no desea usarla. (lea más sobre la agrupación de conexiones de SQL Server (ADO.NET) )
  2. Nunca use el contexto global. ObjectContext implementa internamente varios patrones, incluidos Identity Map y Unit of Work. El impacto del uso del contexto global es diferente según el tipo de aplicación.
  3. Para aplicaciones web, utilice un contexto único por solicitud. Para los servicios web, use un solo contexto por llamada. En la aplicación WinForms o WPF, use un solo contexto por formulario o por presentador. Puede haber algunos requisitos especiales que no permitirán utilizar este enfoque, pero en la mayoría de las situaciones es suficiente.

Si desea saber qué impacto tiene el contexto de un solo objeto para la aplicación WPF / WinForm, consulte este artículo . Se trata de NHibernate Session pero la idea es la misma.

Editar:

Cuando usa EF, por defecto carga cada entidad solo una vez por contexto. La primera consulta crea instancia de entidad y la almacena internamente. Cualquier consulta posterior que requiera una entidad con la misma clave devuelve esta instancia almacenada. Si los valores en el almacén de datos cambiaron, aún recibirá la entidad con valores de la consulta inicial. Esto se llama patrón de mapa de identidad . Puede forzar el contexto del objeto para volver a cargar la entidad, pero volverá a cargar una sola instancia compartida.

Los cambios realizados en la entidad no persisten hasta que llame SaveChangesal contexto. Puede hacer cambios en varias entidades y almacenarlas a la vez. Esto se llama patrón de unidad de trabajo . No puede decir selectivamente qué entidad adjunta modificada desea guardar.

Combina estos dos patrones y verás algunos efectos interesantes. Solo tiene una instancia de entidad para toda la aplicación. Cualquier cambio en la entidad afecta a toda la aplicación, incluso si los cambios aún no son persistentes (comprometidos). En la mayoría de los casos, esto no es lo que quieres. Suponga que tiene un formulario de edición en la aplicación WPF. Está trabajando con la entidad y decide cancelar la edición compleja (cambiar valores, agregar entidades relacionadas, eliminar otras entidades relacionadas, etc.). Pero la entidad ya está modificada en el contexto compartido. ¿Qué harás? Sugerencia: no conozco ningún CancelChanges o UndoChanges en ObjectContext.

Creo que no tenemos que discutir el escenario del servidor. Simplemente compartir una sola entidad entre múltiples solicitudes HTTP o llamadas de servicio web hace que su aplicación sea inútil. Cualquier solicitud puede desencadenar SaveChangesy guardar datos parciales de otra solicitud porque está compartiendo una sola unidad de trabajo entre todos ellos. Esto también tendrá otro problema: el contexto y cualquier manipulación con entidades en el contexto o una conexión de base de datos utilizada por el contexto no es seguro para subprocesos.

Incluso para una aplicación de solo lectura, un contexto global no es una buena opción porque probablemente desee datos nuevos cada vez que consulte la aplicación.

Ladislav Mrnka
fuente
Gracias por su respuesta. ¿Quizás podría explicar por qué es malo usar un solo contexto global? Hace que el acceso paralelo sea más difícil, seguro, pero ¿qué más ...?
Noldorin
Ok, eso está mucho más claro ahora, gracias. Solo para confirmar, aunque un contexto global nunca es realmente apropiado, ¿un contexto único para un "diálogo de edición" o tal puede ser la forma correcta? En otras situaciones, como los servicios web y ASP.NET, los contextos dentro de los métodos solo tienen más sentido. ¿Sobre correcto?
Noldorin
Seguí tu consejo y eliminé el singelton. Ahora recibo otro error: stackoverflow.com/questions/14795899/…
Elad Benda
Entiendo que la implementación del patrón de Unidad de trabajo y la encapsulación de DbContext deberían separar la lógica de negocios y las operaciones de la base de datos. No puedo entender cómo implementar el patrón de Unidad de trabajo y usar TransactionScope solo para algunas operaciones.
Rudolf Dvoracek
44
@RudolfDvoracek: Fácilmente. TransactionScopeno pertenece a la unidad de trabajo, pertenece a la lógica de su negocio porque la lógica misma define la transacción. La unidad de trabajo solo define lo que debe persistir juntos, mientras que el alcance de la transacción le permite usar la persistencia de la unidad de trabajo varias veces dentro de la misma transacción.
Ladislav Mrnka
70

De acuerdo con Daniel Simmons:

Cree una nueva instancia de ObjectContext en una instrucción Using para cada método de servicio para que se elimine antes de que regrese el método. Este paso es crítico para la escalabilidad de su servicio. Se asegura de que las conexiones de la base de datos no se mantengan abiertas a través de las llamadas de servicio y que el estado temporal utilizado por una operación en particular sea recolección de basura cuando finalice esa operación. Entity Framework almacena en caché automáticamente los metadatos y otra información que necesita en el dominio de la aplicación, y ADO.NET agrupa las conexiones de la base de datos, por lo que volver a crear el contexto cada vez es una operación rápida.

Esto es de su artículo completo aquí:

http://msdn.microsoft.com/en-us/magazine/ee335715.aspx

Creo que este consejo se extiende a las solicitudes HTTP, por lo que sería válido para ASP.NET. Una aplicación de cliente pesado con estado, como una aplicación WPF, podría ser el único caso para un contexto "compartido".

Dave Swersky
fuente
Gracias, esa es una cita muy informativa allí. Sin embargo, todavía me pregunto si un contexto compartido (global) sería apropiado incluso para una aplicación WPF de cliente o similar. ¿Hay alguna ventaja incluso en este caso?
Noldorin
No habría ventaja para un contexto global en una aplicación WPF, pero probablemente tampoco habría un perjuicio significativo. Si implementa un contexto global, es posible que deba realizar una gestión manual de las conexiones de la base de datos (cierre explícito de la conexión) en casos de altas tasas de solicitud.
Dave Swersky
1
Correcto; así que, esencialmente, nunca puedo equivocarme al usar múltiples contextos temporales (dado que sé que está ocurriendo la agrupación de conexiones). ... Si estuviera utilizando un contexto global único, ¿no podría la conexión en teoría caer en un punto aleatorio en el tiempo?
Noldorin
1
@Nolodrin: No creo que la conexión se caiga "al azar" ... el riesgo es que las conexiones se mantengan abiertas durante demasiado tiempo y saturen el grupo de conexiones.
Dave Swersky, el
1
Implementar ObjectContext / DbContext IDisposable, por lo tanto, debe estar abierto por el menor tiempo razonable, es mi opinión.
nicodemus13
12

Según la documentación de EF6 (4,5 también): https://msdn.microsoft.com/en-us/data/hh949853#9

9.3 Contexto por solicitud

Los contextos de Entity Framework están destinados a ser utilizados como instancias de corta duración para proporcionar la experiencia de rendimiento más óptima . Se espera que los contextos sean de corta duración y descartados, y como tal se han implementado para ser muy livianos y reutilizar metadatos siempre que sea posible. En los escenarios web es importante tener esto en cuenta y no tener un contexto por más de la duración de una sola solicitud. Del mismo modo, en escenarios no web, el contexto debe descartarse en función de su comprensión de los diferentes niveles de almacenamiento en caché en Entity Framework. En términos generales, se debe evitar tener una instancia de contexto durante toda la vida de la aplicación, así como contextos por hilo y contextos estáticos.

Raj Rao
fuente
2
Sé que esta respuesta ha estado aquí por un tiempo, pero debo decir que me ahorró un montón de dolor de cabeza. Seguí recibiendo el error "Conexión agrupada" al usar EF con Oracle, y no podía entender por qué. Había configurado el dbContext como una variable de clase, instanciandolo en la creación. Cambiarlo a crear el contexto según sea necesario solucionó todos los males de mi mundo. ¡Gracias!
Fletchius
1

El siguiente código ayudó a que mi objeto se actualizara con nuevos valores de la base de datos. El comando Entry (object) .Reload () obliga al objeto a recuperar valores de la base de datos

GM_MEMBERS member = DatabaseObjectContext.GM_MEMBERS.FirstOrDefault(p => p.Username == username && p.ApplicationName == this.ApplicationName);
DatabaseObjectContext.Entry(member).Reload();
HGMamaci
fuente
así como también para colecciones (código VB):CType(myContext, IObjectContextAdapter).ObjectContext.Refresh(RefreshMode.StoreWins,myCustomers)
Ivan Ferrer Villa