2009-01-19 1 views
11

Stellen Sie sich vor mir habe folgende:Wie LINQ Ausführung nicht verschieben, wenn sie in einer Anweisung using

private IEnumerable MyFunc(parameter a) 
{ 
    using(MyDataContext dc = new MyDataContext) 
    { 
     return dc.tablename.Select(row => row.parameter == a); 
    } 
} 

private void UsingFunc() 
{ 
    var result = MyFunc(new a()); 

    foreach(var row in result) 
    { 
     //Do something 
    } 
} 

die Dokumentation Nach der Linq Ausführung aufschieben, bis ich das Ergebnis tatsächlich aufzuzählen, die an der foreach in der Leitung auftritt, . Die using-Anweisung sollte jedoch das zuverlässige Erfassen des Objekts am Ende des Aufrufs von MyFunct() erzwingen.

Was passiert eigentlich, wann wird der Entsorger laufen und/oder das Ergebnis laufen?

Ich kann nur daran denken, dass die verzögerte Ausführung zur Kompilierzeit berechnet wird, so dass der eigentliche Aufruf vom Compiler in die erste Zeile der foreach verschoben wird, wodurch die Verwendung korrekt ausgeführt wird, aber nicht bis zur foreach ausgeführt wird Linie? Gibt es einen Guru da draußen, der helfen kann?

EDIT: HINWEIS: Dieser Code funktioniert, ich verstehe einfach nicht wie.

Ich habe etwas gelesen und ich erkannte in meinem Code, dass ich die ToList() - Erweiterung Methode aufgerufen hatte, die natürlich das Ergebnis aufzählt. Das Verhalten der getippten Antwort ist für die tatsächlich beantwortete Frage vollkommen korrekt.

Entschuldigung für irgendeine Verwirrung.

+0

nette Frage. Ich wurde von diesem bombardiert .. ein Using (DataContext) explodiert spektakulär – Gishu

+0

Ich bekomme eine ObjectDisposedException "DataContext zugegriffen nach Dispose." wenn ich das versuche. Ist Ihr MyDataContext der automatisch generierte DataContext vom LINQ-Designer? Erbt es von DataContext? –

+0

Ich drittens die Idee: Wenn Sie Ihren Datenkontext mit "Using" ablegen, erhalten Sie eine Laufzeitausnahme, wenn Sie versuchen, den Datenkontext später zu verwenden. Ich weiß, dass ich es getan habe. –

Antwort

12

Ich würde erwarten, dass einfach nicht funktioniert; Die Select ist zurückgestellt, so dass zu diesem Zeitpunkt keine Daten verbraucht wurden. Da Sie jedoch den Datenkontext (vor dem Verlassen MyFunc) entsorgt haben, wird es niemals Daten abrufen können. Eine bessere Option ist es, den Datenkontext in die Methode zu übergeben, so dass der Verbraucher die Lebensdauer wählen kann. Auch würde ich empfehlen IQueryable<T> so dass der Verbraucher „komponieren“ das Ergebnis der Rückkehr (dh OrderBy/Skip/Take/Where usw. hinzufügen und haben es die letzte Abfrage auswirken):

// this could also be an instance method on the data-context 
internal static IQueryable<SomeType> MyFunc(
    this MyDataContext dc, parameter a) 
{ 
    return dc.tablename.Where(row => row.parameter == a); 
} 

private void UsingFunc() 
{ 
    using(MyDataContext dc = new MyDataContext()) { 
     var result = dc.MyFunc(new a()); 

     foreach(var row in result) 
     { 
      //Do something 
     } 
    } 
} 

Update: Wenn Sie (Kommentare) die Ausführung nicht verzögern möchten (dh Sie möchten nicht, dass der Aufrufer den Datenkontext behandelt), müssen Sie die Ergebnisse auswerten. Sie können dies tun, indem Sie .ToList() oder .ToArray() auf das Ergebnis aufrufen, um die Werte zu puffern.

private IEnumerable<SomeType> MyFunc(parameter a) 
{ 
    using(MyDataContext dc = new MyDataContext) 
    { 
     // or ToList() etc 
     return dc.tablename.Where(row => row.parameter == a).ToArray(); 
    } 
} 

Wenn Sie wollen, dass es in diesem Fall verschoben halten, dann müssen Sie ein „Iteratorblock“ verwenden:

private IEnumerable<SomeType> MyFunc(parameter a) 
{ 
    using(MyDataContext dc = new MyDataContext) 
    { 
     foreach(SomeType row in dc 
      .tablename.Where(row => row.parameter == a)) 
     { 
     yield return row; 
     } 
    } 
} 

latente Dies wird nun ohne um die Daten-Kontext übergeben.

+0

Ich versuche, die Datenbankoperationen aus der Geschäftslogik herauszuhalten, also versuche ich, den Datenkontext nicht zu umgehen. Alles, was ich will, sind Listen, Enumerables und Skalare aus diesen Funktionen. – Spence

+0

Siehe Update, dann –

+0

Würde das jedes Mal die Datenbank treffen? Oder wird es die Abfrage tun und dann jeden Wert zurückgeben? – Spence

8

Ich habe gerade gebucht einen weiteren latenten Ausführungs Lösung für dieses Problem here, einschließlich dieses Codebeispiel:

IQueryable<MyType> MyFunc(string myValue) 
{ 
    return from dc in new MyDataContext().Use() 
      from row in dc.MyTable 
      where row.MyField == myValue 
      select row; 
} 

void UsingFunc() 
{ 
    var result = MyFunc("MyValue").OrderBy(row => row.SortOrder); 
    foreach(var row in result) 
    { 
     //Do something 
    } 
} 

Die Use() Extension-Methode verhält sich wie im Wesentlichen latente einem using Block.

+0

Ich habe die Antwort bereits akzeptiert, aber das ist eine wirklich nette Art zu denken. .Use() - Erweiterungsmethode. Du verdienst mehr als ein +1 für eine solche Antwort. – Spence

Verwandte Themen