c# - funciona - que es el protocolo tcp/ip
EF: el contexto no se puede usar mientras se crea la excepción del modelo durante las solicitudes HTTP (4)
Recibo "El contexto no se puede utilizar mientras se está creando el modelo". problema en mi aplicación web en una de mis páginas web. Esta página web en particular se envía al servidor cada 2-3 segundos para actualizar la pantalla. De mis pruebas descubrí que si tengo 2 o más instancias de navegador abiertas en esta página, después de varios minutos recibo una excepción "El contexto no se puede usar mientras se está creando el modelo" desde lo profundo del repositorio.
Este código llama a un "servicio" para recuperar los datos necesarios. Este código se ejecuta en un atributo de autorización personalizado de la clase MVC Controller.
// Code in custom "Authorization" attribute on the controller
int? stationId = stationCookieValue; // Read value from cookie
RoomStationModel roomStationModel = RoomStationService.GetRoomStation(stationId); // Error occurs inside this call
Aquí está el "RoomStationModel"
public class RoomStationModel
{
[Key]
public int RoomStationId { get; set; }
public int? RoomId { get; set; }
[ForeignKey("RoomId")]
public virtual RoomModel Room { get; set; }
/* Some other data properties.... */
}
public class RoomModel
{
[Key]
public int RoomId { get; set; }
public virtual ICollection<RoomStationModel> Stations { get; set; }
}
Aquí está el código para la llamada de servicio arriba:
public RoomStationModel GetRoomStation(int? roomStationId)
{
RoomStationModel roomStationModel = null;
if (roomStationId.HasValue)
{
using (IRepository<RoomStationModel> roomStationRepo = new Repository<RoomStationModel>(Context))
{
roomStationModel = roomStationRepo.FirstOrDefault(rs => rs.RoomStationId == roomStationId.Value, false, new string[] { "Room" });
}
}
return roomStationModel;
}
Aquí está el repositorio .... donde se produce el error
public class Repository<TObject> : IRepository<TObject> where TObject : class
{
protected MyContext Context = null;
public Repository(IDataContext context)
{
Context = context as MyContext;
}
protected DbSet<TObject> DbSet { get { return Context.Set<TObject>(); } }
public virtual TObject FirstOrDefault(Expression<Func<TObject, bool>> predicate, bool track = true, string[] children = null)
{
var objectSet = DbSet.AsQueryable();
if (children != null)
foreach (string child in children)
objectSet = objectSet.Include(child);
if (track)
return objectSet.Where(predicate).FirstOrDefault<TObject>(predicate);
return objectSet.Where(predicate).AsNoTracking().FirstOrDefault<TObject>(predicate);
}
}
Captura de pantalla del error:
Stacktrace :
at System.Data.Entity.Internal.LazyInternalContext.InitializeContext()
at System.Data.Entity.Internal.InternalContext.Initialize()
at System.Data.Entity.Internal.InternalContext.GetEntitySetAndBaseTypeForType(Type entityType)
at System.Data.Entity.Internal.Linq.InternalSet`1.Initialize()
at System.Data.Entity.Internal.Linq.InternalSet`1.Include(String path)
at System.Data.Entity.Infrastructure.DbQuery`1.Include(String path)
at System.Data.Entity.DbExtensions.Include[T](IQueryable`1 source, String path)
at Vanguard.AssetManager.Data.Repository`1.FirstOrDefault(Expression`1 predicate, Boolean track, String[] children) in C:/Work/VanguardAssetManager/Main/Vanguard.AssetManager.Data/Repository.cs:line 100
at Vanguard.AssetManager.Services.Business.RoomStationService.GetRoomStation(Nullable`1 roomStationId) in C:/Work/VanguardAssetManager/Main/Vanguard.AssetManager.Services/Business/RoomStationService.cs:line 61
at Vanguard.AssetManager.Web.Attributes.RoomStationAuthorizeAttribute.OnAuthorization(AuthorizationContext filterContext) in C:/Work/VanguardAssetManager/Main/Vanguard.AssetManager.Web/Attributes/RoomStationAuthorizeAttribute.cs:line 52
at System.Web.Mvc.ControllerActionInvoker.InvokeAuthorizationFilters(ControllerContext controllerContext, IList`1 filters, ActionDescriptor actionDescriptor)
at System.Web.Mvc.ControllerActionInvoker.InvokeAction(ControllerContext controllerContext, String actionName)
Versión EF: 4.1 (Código primero)
Encontré este error y parece que lo resolví al proporcionar un reemplazo al método Dispose () en el controlador. Parece que forzar el cierre de la conexión de la base de datos antes de intentar abrir una nueva subvierte este error.
protected override void Dispose(bool disposing)
{
if(disposing)
{
_fooRepository.Dispose();
}
base.Dispose(disposing);
}
Esto parece ser una de dos cosas, una condición de raza de algún tipo o un problema de "alcance del contexto". Debe asegurarse de que el contexto se está inicializando de forma segura para los subprocesos y de que los subprocesos diferentes no pueden acceder al contexto para evitar condiciones de carrera. Una causa difícil de detectar de este error es también el acceso del propio modelo en la modificación de OnModelCreation.
Experimenté este problema hoy. El problema fue que accidentalmente estaba usando la misma instancia de mi DbContext en todas las solicitudes. La primera solicitud crearía la instancia y comenzaría a construir el modelo, y la segunda solicitud entraría e intentaría recuperar los datos mientras aún se estaba construyendo.
Mi error fue tonto. Accidentalmente utilicé HttpContext.Current.Cache en lugar de HttpContext.Current.Items :)
Su repositorio es de corta duración (lo crea para cada llamada a GetRoomStation()
pero su contexto real parece ser de larga duración (propiedad RoomServiceStation.Context
). Esto significa que cada llamada a su aplicación web usará el mismo contexto .
Este es el escenario de "EF en un nivel N" en el que está intentando mantener algo de estado (el contexto) en el modelo arquitectónico sin estado de una aplicación web. Todas esas solicitudes se están canalizando al mismo contexto en diferentes hilos y obtienes una condición de carrera.
Un hilo podría iniciar la inicialización por primera vez de su contexto en respuesta a una solicitud, y otro viene al intentar usar el contexto. La segunda solicitud cree que el contexto está listo para su uso y usted obtiene esta excepción. Incluso puede obtener esto si tiene múltiples contextos intentando "girar" al mismo tiempo que se sugiere en otro subproceso SO .
Puedes hacer algunas cosas. Podría intentar un bloqueo pesimista en el acceso a su contexto, pero está poniendo un cuello de botella innecesario. Puede intentar crear una especie de código "antes de que los clientes me llamen, inicializar el contexto", pero debe encontrar un buen lugar para hacerlo, tal vez utilizando el método de "fuerza bruta" sugerido en un subproceso de MSDN .
Una cosa mejor que hacer es simplemente crear un nuevo contexto para cada solicitud a su servicio de back-end. Hay algunos gastos generales, sí, pero mínimos. La sobrecarga es probablemente menos probable que anule el rendimiento que el bloqueo pesimista, y no estará sujeta a eventos de reciclaje del grupo de aplicaciones que amplíen su aplicación web en una granja y así sucesivamente.
Si confía en el seguimiento de cambios u otra naturaleza de estado de contexto, perderá este beneficio. En este caso, tendrá que encontrar un mecanismo diferente para rastrear y minimizar los accesos a la base de datos.
De un artículo de MSDN esto se resume (énfasis mío):
Si serializa entidades de un nivel a otro, el patrón recomendado es mantener el contexto en el nivel medio solo lo suficiente para una llamada de método de servicio único. Las llamadas subsiguientes generarán una nueva instancia del contexto para completar cada tarea.
Un hilo en EF / WCF / N-tier también puede darte algunas ideas , y la publicación # 5 del blog de Jorge habla sobre EF en N-niveles (toda la serie puede ser una buena lectura). Y, por cierto, he encontrado exactamente lo mismo: muchos clientes golpean el contexto al mismo tiempo, lo que resulta en este problema.