wake-up-neo.com

EF - Der Kontext kann nicht verwendet werden, während das Modell während HTTP-Anforderungen eine Ausnahme erstellt

Ich empfange "Der Kontext kann nicht verwendet werden, während das Modell erstellt wird." Problem in meiner Webanwendung auf einer meiner Webseiten. Diese Webseite pOSTet alle 2-3 Sekunden zum Server, um den Bildschirm zu aktualisieren. Bei meinen Tests habe ich festgestellt, dass wenn zwei oder mehr Browserinstanzen für diese Seite geöffnet sind, nach einigen Minuten die Ausnahme "Der Kontext kann nicht verwendet werden, während das Modell erstellt wird" aus der Tiefe des Repositorys angezeigt wird.

Dieser Code ruft einen "Dienst" auf, um die benötigten Daten abzurufen. Dieser Code wird in einem benutzerdefinierten Berechtigungsattribut der MVC Controller-Klasse ausgeführt.

// 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

Hier ist das "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; }
}

Hier ist der Code für den Serviceabruf oben:

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;
}

Hier ist das Repository ... wo der Fehler auftritt

    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);
    }
}

Screenshot des Fehlers: Screenshot of error occurring

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)

EF Version: 4.1 (Code zuerst)

19
contactmatt

Ihr Repository ist von kurzer Dauer (Sie erstellen es für jeden Aufruf von GetRoomStation(), aber Ihr tatsächlicher Kontext scheint langlebig zu sein (RoomServiceStation.Context-Eigenschaft). Das bedeutet, dass jeder Aufruf Ihrer Webanwendung denselben Kontext verwendet.

Dies ist das Szenario "EF in einer N-Schicht", in dem Sie versuchen, im architektonisch zustandslosen Modell einer Webanwendung etwas Zustand zu erhalten (den Kontext). Alle diese Anforderungen werden in verschiedenen Threads in denselben Kontext geleitet und Sie erhalten eine Race-Bedingung.

Ein Thread könnte die erste Initialisierung Ihres Kontexts als Antwort auf eine Anforderung starten, und ein anderer kommt beim Versuch, den Kontext zu verwenden. Die zweite Anforderung hält den Kontext für die Verwendung bereit und Sie erhalten diese Ausnahme. Sie können dies sogar erhalten, wenn Sie mehrere Kontexte haben, die gleichzeitig mit dem vorgeschlagenen in einem anderen SO thread versuchen, "hochzufahren".

Sie können ein paar Dinge tun. Sie könnten ein pessimistisches Sperren des Zugriffs auf Ihren Kontext versuchen, aber Sie haben einen unnötigen Engpass. Sie könnten versuchen, eine Art "Code zu erstellen, bevor Clients mich anrufen, den Kontext zu initialisieren", aber Sie müssen einen geeigneten Ort dafür finden, vielleicht mit der "brute force" -Methode in einem MSDN-Thread vorgeschlagen .

Besser ist es, einfach einen neuen Kontext für every -Anforderung an Ihren Back-End-Service zu erstellen. Es gibt etwas Overhead, ja, aber minimal. Es ist wahrscheinlich weniger wahrscheinlich, dass der Overhead die Leistung als pessimistisches Sperren abbricht, und es unterliegt nicht dem Recycling von App-Pool-Ereignissen, die Ihre Web-App in einer Farm usw. skalieren.

Wenn Sie sich auf Änderungsverfolgung oder andere zustandsorientierte Natur eines Kontexts verlassen, verlieren Sie diesen Vorteil. In diesem Fall müssen Sie einen anderen Mechanismus zur Verfolgung und Minimierung von Datenbankzugriffen finden.

Aus einem MSDN-Artikel Dies wird zusammengefasst (Hervorhebung meines):

Wenn Sie Entitäten von einer Schicht in eine andere serialisieren, empfiehlt es sich, das Muster den Kontext in der mittleren Schicht nur lang genug Für einen einzelnen Aufruf der Servicemethode zu belassen. Bei nachfolgenden Aufrufen wird eine neue Instanz des Kontexts gestartet, um die einzelnen Aufgaben abzuschließen.

Ein Thread zu EF/WCF/N-Tier kann Ihnen auch einige Einblicke gewähren und Jorge's Blog Post # 5 spricht über EF in N-Tiers (die ganze Serie könnte eine gute Lektüre sein). Übrigens bin ich auf genau dasselbe gestoßen: Viele Kunden treffen gleichzeitig den Kontext, was zu diesem Problem führt.

30
CRAGIN

Ich bin auf diesen Fehler gestoßen und habe ihn scheinbar gelöst, indem er die Dispose () - Methode im Controller überschreibt. Es scheint, als würde die Schließung der Datenbankverbindung vor dem Versuch, eine neue zu öffnen, diesen Fehler unterdrücken.

protected override void Dispose(bool disposing)
{
   if(disposing)
   {
        _fooRepository.Dispose();
   }
   base.Dispose(disposing);
}
1
Matt Broyles

Ich habe dieses Problem heute erlebt. Das Problem war, dass ich versehentlich dieselbe Instanz von DbContext für Anfragen verwendet habe. Die erste Anforderung würde die Instanz erstellen und mit dem Aufbau des Modells beginnen, und die zweite Anforderung würde eingehen und versuchen, Daten abzurufen, während sie noch aufgebaut wurde.

Mein Fehler war dumm. Ich habe versehentlich HttpContext.Current.Cache anstelle von HttpContext.Current.Items verwendet :)

0
Peter Morris

Dies scheint eines von zwei Dingen zu sein, eine Art Rassenbedingung oder ein "Kontext-Scoping-Problem". Sie sollten sicherstellen, dass der Kontext auf eine Thread-sichere Weise initialisiert wird und dass der Kontext nicht von verschiedenen Threads aufgerufen wird, um Race-Bedingungen zu verhindern. Eine schwer zu erfassende Ursache für diesen Fehler ist auch der Zugriff auf das Modell selbst in der OnModelCreation-Überschreibung.

0
JTMon