2010-09-24 7 views
12

Ich habe eine statische Klasse mit einer statischen get Eigenschaft, und in dieser Eigenschaft Ich tue dies:C#: Hinzufügen von Rahmen zu Parallel.ForEach() in ASP.NET

// property body 
{ 
    // HttpContext.Current is NOT null 
    ... 

    Parallel.ForEach(files, file => 
    { 
     // HttpContext.Current is null 
     var promo = new Promotion(); 
     ... 
    }); 
    ... 

    // HttpContext.Current is NOT null 
} 

Diese statische Klasse nicht Typinitialisierung durchlaufen, bis eine Sicht diese Eigenschaft verwendet.

Das Problem besteht darin, dass Promotion ‚s statischer Konstruktor, der das erste Mal initialisiert wird ein new Promotion() innerhalb der Parallel.ForEach() erstellt wird, HttpContext.Current verwendet. Wenn promo im Rahmen dieser Parallel.ForEach() instanziiert wird, ist HttpContext.Currentnull, und new Promotion() verursacht daher eine Ausnahme.

HttpContext.Current ist nicht NULL innerhalb der statischen Get-Eigenschaft, weil es nicht aufgerufen wird, bis die Ansicht es verwendet (und es gibt daher eine HttpContext.Current).

Wenn Promotion verwendet HttpContext.Current in seiner Instanzen anstelle seiner statischen Mitglieder, könnte ich wahrscheinlich nur HttpContext.Current in den new Promotion() Konstruktor übergeben:

var context = HttpContext.Current; 
Parallel.ForEach(files, file => 
{ 
    var promo = new Promotion(context); 
}); 

Aber da static Mitglieder der Promotion HttpContext.Current benötigen, kann ich nicht . Ich könnte wahrscheinlich die Promotion Klasse ändern, um die statischen Mitglieder zu ändern, die es als Instanzmitglieder benötigen, aber sie sind aus einem Grund statisch - es würde eine große Leistungseinbuße geben, wenn alle Mitglieder, die statisch waren, stattdessen auf jedem definiert werden mussten Instanz jedes Mal ein new Promotion wurde instanziiert.

Was sind die möglichen Problemumgehungen dafür? Ich habe nicht realisiert HttpContext.Current wäre Null im Rahmen der Parallel.ForEach().

+0

statische Mitglieder von Promotion werden nur einmal initialisiert, wenn der Code der Klasse zum ersten Mal berührt wird .... Wie können sie von 'HttpContext.Current' abhängen? –

+0

Liegt der Quellcode für "Promotion" unter Ihrer Kontrolle? –

Antwort

11

HttpContext.Current ist null, da es in "Nicht-Web-Threads" ausgeführt wird. Wenn Sie etwas Code mit new Thread(...) gabelten, wäre es genau das gleiche. Die TPL verbirgt dies etwas, aber Sie müssen immer noch erkennen, dass jede Iteration in Ihrem Parallel.ForEach möglicherweise in einem anderen Thread ausgeführt werden kann, und behandeln Sie es entsprechend.

Insbesondere wenn Sie eine Klasse oder Methode aus der Web-Anfrage (und Parallel.ForEach ist eine solche Verwendung) verwenden möchten, können Sie einfach nicht HttpContext.Current verwenden. Eine Problemumgehung ist das explizite Übergeben des HttpContext (oder HttpContextBase für verbesserte Testbarkeit) in dem Konstruktor (oder als einen Methodenparameter)

Kurz zusammengefasst: Sie müssen aus HttpContext.Current statisch ausbrechen.

0

Wie Mauricio darauf hinweist, hängt der HttpContext.Current vom aktuell ausgeführten Thread ab. Es fällt mir auf, dass ein statischer Konstruktor von solch einem inhärent vorübergehenden Wert wie HttpContext.Current abhängt, aber vielleicht war das nicht Ihre Idee.

Wenn Sie die Promotion Klasse ändern können, wäre das die erste Option, die ich in Betracht ziehen würde.

Wenn nicht, müssen Sie irgendwie die Initialisierung des Typs Promotion an einem Punkt erzwingen, an dem HttpContext.Current noch gültig ist. Um zu erfahren, welche Art von Force-Initialisierung zu lesen ist, lesen Sie this Jon Skeet blog post.

Eine Option könnte sein, ein dummy Promotion Objekt zu erstellen, (nur einmal im gesamten Programm sollte genug sein). Wenn das keine Option ist, können Sie versuchen, die Eigenschaft mit Reflektion zu lesen. Ich weiß nicht, ob das eine Initialisierung des Typs erzwingt, aber ich denke schon.

4

Übergeben Sie einfach jeden Kontext, den Sie außerhalb des Parallel.ForEach-Aufrufs haben, in alle Funktionen, die Sie aufrufen, die sich auf diesen Kontext beziehen.

var context = HttpContext.Current; 
Parallel.ForEach(items, item => 
    { 
     DoSomething(item, context); 
    } 
); 



private static void DoSomething(item, context = null) { 
    if (context == null) context = HttpContext.Current; 

    ... 
} 

Ich mag Rückfall haben auf null, so etwa habe ich keine Sorgen zu machen um den Kontext die ganze Zeit vorbei. Ich erinnere mich nur daran, dass meine Funktionen Kontext benötigen, wenn ich von einem anderen Thread aus anrufe, und dann klicke ich das Baby genau dort rein.

0

Es funktioniert nicht, da innerhalb der Foreach ein neuer Thread erstellt wird, so dass der Kontext null ist. Selbst wenn Sie eine Methode DoSomething erstellen, um den aktuellen Kontext festzulegen, ist der Kontext immer noch null.

Verwandte Themen