2012-11-29 14 views
28

Ich bin neu in EF und ich versuche, eine Erweiterungs-Methode zu verwenden, die von meinem Datenbanktyp User in meine Info-Klasse UserInfo konvertiert.
Ich benutze zuerst Datenbank, wenn das einen Unterschied macht?Der Vorgang kann nicht abgeschlossen werden, da der DbContext Fehler wurde

Mein Code unten gibt dem Fehler

Der Vorgang kann nicht abgeschlossen werden, da die DbContext angeordnet wurde.

try 
{ 
    IQueryable<User> users; 
    using (var dataContext = new dataContext()) 
    { 
     users = dataContext.Users 
        .Where(x => x.AccountID == accountId && x.IsAdmin == false); 
     if(users.Any() == false) 
     { 
      return null; 
     } 
    } 
    return users.Select(x => x.ToInfo()).ToList(); // this line is the problem 
} 
catch (Exception ex) 
{ 
    //... 
} 

Ich kann sehen, warum es es tun würde, aber ich verstehe nicht, warum auch das Ergebnis der where-Anweisung nicht in das users Objekt gespeichert wird?

Also meine wichtigste Frage ist, warum funktioniert es nicht und zweitens, was ist der richtige Weg, um Erweiterungsmethoden und EF zu verwenden?

Antwort

29

Diese question & answer führte mich zu der Annahme, dass IQueryable einen aktiven Kontext für seine Operation benötigen. Das heißt, Sie sollten versuchen, diese stattdessen:

try 
{ 
    IQueryable<User> users; 

    using (var dataContext = new dataContext()) 
    { 
     users = dataContext.Users.Where(x => x.AccountID == accountId && x.IsAdmin == false); 

     if(users.Any() == false) 
     { 
      return null; 
     } 
     else 
     { 
      return users.Select(x => x.ToInfo()).ToList(); // this line is the problem 
     } 
    } 


} 
catch (Exception ex) 
{ 
    ... 
} 
+1

Danke. Ich kann jetzt sehen, dass IQueryable ist, wo ich in Schwierigkeiten gerate. Wenn ich die users.Select() ... Zeile in die using-Anweisung setzen, gibt es mir einen anderen Fehler (andere Frage, die ich denke). Also habe ich den Rückgabetyp IEnumerable geändert und das hat mein Problem gelöst. – Colin

+0

Danke, das ist für mich funktioniert und jetzt kam ich zu wissen, dass IQueryable Active DBContext –

2

Der Grund, warum es den Fehler zu werfen ist das Objekt angeordnet ist und nach, dass wir versuchen, die Tabellenwerte durch das Objekt zuzugreifen, sondern Objekt ist disposed.Better zu wandeln Sie das in ToList() um, damit wir Werte haben können

Vielleicht ist es nicht wirklich die Daten erhalten, bis Sie es verwenden (es ist lazy loading), so dass DataContext nicht existiert, wenn Sie versuchen, die Arbeit zu tun . Ich wette, wenn Sie die ToList() im Umfang getan haben, wäre es in Ordnung.

try 
{ 
    IQueryable<User> users; 
    var ret = null; 

    using (var dataContext = new dataContext()) 
    { 
     users = dataContext.Users.Where(x => x.AccountID == accountId && x.IsAdmin == false); 

     if(users.Any()) 
     { 
      ret = users.Select(x => x.ToInfo()).ToList(); 
     } 

    } 

    Return ret; 
} 
catch (Exception ex) 
{ 
    ... 
} 
22

Objekte als IQueryable<T> belichtet und IEnumerable<T> nicht wirklich „execute“, bis sie über oder auf andere Weise zugegriffen wird iteriert werden, wie beispielsweise in eine List<T> besteht. Wenn EF IQueryable<T> zurückgibt, besteht es im Wesentlichen nur darin, etwas zu komponieren, das Daten abrufen kann, es führt den Abruf nicht durch, bis Sie es verbrauchen.

Sie können ein Gefühl dafür bekommen, indem Sie einen Haltepunkt setzen, wo die IQueryable definiert ist, vs wenn die .ToList() aufgerufen wird. (Aus dem Rahmen des Datenkontexts, wie Jofry richtig darauf hingewiesen hat.) Die Arbeit, um die Daten zu ziehen, ist während des Aufrufs ToList() erledigt.

Aus diesem Grund müssen Sie die IQueryable<T> im Rahmen des Datenkontextes behalten.

12

Sie müssen sich daran erinnern, dass IQueryable-Abfragen nicht wirklich für den Datenspeicher ausgeführt werden, bis Sie sie aufzählen.

using (var dataContext = new dataContext()) 
{ 

Diese Codezeile nicht wirklich etwas anderes tun, als die SQL-Anweisung bauen

users = dataContext.Users.Where(x => x.AccountID == accountId && x.IsAdmin == false); 

.Any() ist eine Operation, die die IQueryable aufzählt, so wird die SQL an die Daten gesendet Quelle (über DataContext) und dann die.Jegliche() Operationen ausgeführt dagegen

if(users.Any() == false) 
    { 
     return null; 
    } 
} 

Ihr „Problem“ -Linie ist die SQL-oben gebaut Wiederverwendung, und dann tun, einen zusätzlichen Arbeitsgang (.Select()), die auf die Abfrage kommt noch hinzu,. Wenn Sie es verlassen haben hier keine Ausnahme, außer Ihr Problem Linie

return users.Select(x => x.ToInfo()).ToList(); // this line is the problem 

ruft .ToList(), die die IQueryable aufzählt, die die SQL führt zur Datenquelle durch die Datacontext gesendet werden sollen, die im Original verwendet wurde LINQ-Abfrage Da dieser dataContext entsorgt wurde, ist er nicht mehr gültig und .ToList() löst eine Ausnahme aus.

Das ist das "warum es nicht funktioniert". Die Lösung besteht darin, diese Codezeile in den Bereich Ihres dataContext zu verschieben.

Wie es richtig nutzen eine andere Frage mit ein paar wohl richtigen Antworten, die auf Ihre Bewerbung (Forms vs. ASP.net vs. MVC, etc.) abhängen. Das Muster, das dies implementiert, ist das Muster der Arbeitseinheit. Es gibt fast keine Kosten für das Erstellen eines neuen Kontextobjekts. Daher besteht die allgemeine Regel darin, ein neues Kontextobjekt zu erstellen, zu bearbeiten und dann zu verwerfen. In Web-Apps erstellen manche Personen einen Kontext pro Anfrage.

+0

SingleOrDefault() auch ähnliche Probleme verursachen benötigt – code4j

0

Dies kann so einfach sein wie das Hinzufügen von ToList() in Ihrem Repository. Zum Beispiel:

public IEnumerable<MyObject> GetMyObjectsForId(string id) 
{ 
    using (var ctxt = new RcContext()) 
    { 
     // causes an error 
     return ctxt.MyObjects.Where(x => x.MyObjects.Id == id); 
    } 
} 

Wird die Db Context angeordnet Fehler in der anrufenden Klasse ergeben, aber dies kann durch Zugabe von ToList() auf der LINQ Operation explizit Ausübung der Aufzählung gelöst werden: Das

public IEnumerable<MyObject> GetMyObjectsForId(string id) 
{ 
    using (var ctxt = new RcContext()) 
    { 
     return ctxt.MyObjects.Where(x => x.MyObjects.Id == id).ToList(); 
    } 
} 
1

ändern :

using (var dataContext = new dataContext()) 
{ 
    users = dataContext.Users.Where(x => x.AccountID == accountId && x.IsAdmin == false); 

    if(users.Any()) 
    { 
     ret = users.Select(x => x.ToInfo()).ToList(); 
    } 

} 

dazu:

using (var dataContext = new dataContext()) 
{ 
    return = dataContext.Users.Where(x => x.AccountID == accountId && x.IsAdmin == false).Select(x => x.ToInfo()).ToList(); 
} 

Der Kernpunkt besteht darin, dass Sie die Aufzählung des Kontextdatensatzes nur einmal erzwingen möchten. Lassen Sie den Anrufer mit dem leeren Mengen-Szenario fertig werden, wie es sein sollte.

1

Hier versuchen Sie, IQueryable-Objekt auf inaktivem DBContext auszuführen. Ihr DBcontext ist bereits entsorgt. Sie können das IQueryable-Objekt nur ausführen, bevor DBContext entsorgt wird. Bedeutet, dass Sie schreiben müssen users.Select(x => x.ToInfo()).ToList() Anweisung im Inneren mit Bereich

Verwandte Themen