Hinzufügen von Kontext zu Parallel.ForEach () in ASP.NET

Ich habe eine statische class mit einer statischen get-Eigenschaft, und in dieser Eigenschaft mache ich Folgendes:

// 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 class hat erst eine Typinitialisierung durchlaufen, bis eine Ansicht diese Eigenschaft verwendet.

Das Problem ist, dass der statische Konstruktor von Promotion , der beim ersten HttpContext.Current einer new Promotion() innerhalb von Parallel.ForEach() HttpContext.Current , HttpContext.Current . Wenn promo im Rahmen dieser Parallel.ForEach() HttpContext.Current wird, hat HttpContext.Current , und new Promotion() verursacht daher eine Ausnahme.

HttpContext.Current ist in der statischen get HttpContext.Current nicht HttpContext.Current , da es nicht aufgerufen wird, bis die Ansicht es verwendet (und daher ein HttpContext.Current ).

Wenn Promotion in seinen Instanzen HttpContext.Current anstelle seiner statischen Member verwendet, könnte ich HttpContext.Current möglicherweise einfach in den new Promotion() Konstruktor von new Promotion() :

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

Aber da static Mitglieder von Promotion HttpContext.Current benötigen, kann ich das nicht. Ich könnte die Promotion class wahrscheinlich umgestalten, um die statischen Member zu ändern, die als Instanz-Member erforderlich sind, aber sie sind aus einem bestimmten Grund statisch – es würde einen erheblichen performancesnachteil bedeuten, wenn alle statischen Member für jedes Element definiert werden müssten Jedes Mal, wenn eine new Promotion instanziiert wurde.

Was sind die möglichen Problemumgehungen dafür? Ich wusste nicht, dass HttpContext.Current im Rahmen von Parallel.ForEach() null HttpContext.Current würde.

HttpContext.Current ist null, da es in “Nicht-Web-Threads” ausgeführt wird. Wenn Sie einen Code mit Hilfe des new Thread(...) es genauso. Die TPL verbirgt dies etwas, aber Sie müssen immer noch erkennen, dass jede Iteration in Parallel.ForEach möglicherweise in einem anderen Thread ausgeführt und entsprechend behandelt werden kann.

Wenn Sie eine class oder Methode aus der Webanforderung verwenden möchten (und Parallel.ForEach eine solche Verwendung findet), können Sie insbesondere HttpContext.Current nicht verwenden. Eine Problemumgehung besteht darin, HttpContext (oder HttpContextBase für verbesserte Testbarkeit) im Konstruktor (oder als Methodenparameter) explizit zu übergeben.

Kurz gesagt: HttpContext.Current muss statisch nicht verwendet werden.

Übergeben Sie einfach jeden Kontext, den Sie außerhalb des Parallel.ForEach-Aufrufs haben, in alle functionen, die Sie in diesem Kontext aufrufen.

 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 es, einen Rückfall auf Null zu haben, so dass ich mich nicht ständig darum kümmern muss, den Kontext zu umgehen. Ich muss nur daran denken, dass meine functionen einen Kontext benötigen, wenn Sie von einem anderen Thread aus anrufen, und dann klopfe ich das Baby genau dort an.

Wie Mauricio darauf hinweist, hängt HttpContext.Current vom aktuell ausgeführten Thread ab. Es erscheint mir ungewöhnlich, dass ein statischer Konstruktor von einem solchen inhärenten Übergangswert wie HttpContext.Current , aber das war vielleicht nicht Ihre Idee.

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

Wenn nicht, müssen Sie die HttpContext.Current für Promotion an einem Punkt HttpContext.Current an dem HttpContext.Current noch gültig ist. Lesen Sie diesen Jon Skeet-Blogeintrag, um zu erfahren, welche Initialisierung für Kräfte verwendet wird.

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

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