2012-11-26 6 views
6

Ich erstelle eine Reihe von von ASP.Net gehosteten WebAPI-Diensten, die eine alte Bibliothek verwenden müssen, die stark von HttpContext.Current abhängig ist. Ich habe Probleme, sicherzustellen, dass der Kontext in allen Methoden erhalten bleibt, die an einem asynchronen Aufruf teilnehmen. Ich habe verschiedene Varianten mit warte/Task.Wait und TaskScheduler.FromCurrentSynchronizationContext() auf den folgenden Code ausprobiert.HttpContext bei asynchronen Verbindungen mit WebAPI (Medium Trust) beibehalten

[HttpGet] 
    public Task<IEnumerable<string>> ContinueWith() 
    { 
     Thread.CurrentThread.CurrentCulture = new CultureInfo("fr-FR"); //or another culture that is not the default on your machine 
     Thread.CurrentThread.CurrentUICulture = Thread.CurrentThread.CurrentCulture; 

     var output = new List<string> { TestOutput("Action start") }; 

     var task = Task.Factory.StartNew(() => 
      { 
       Thread.Sleep(1000); 
       return TestOutput("In Task"); 
      }).ContinueWith(slowString => 
      { 
       output.Add(slowString.Result); 

       output.Add(TestOutput("Action end")); 
       return output as IEnumerable<string>; 
      }); 

     output.Add(TestOutput("Action Mid")); 

     return task; 
    } 

    private string TestOutput(string label) 
    { 
     var s = label + " ThreadID: " + Thread.CurrentThread.ManagedThreadId.ToString(CultureInfo.InvariantCulture); 
     s += " " + Thread.CurrentThread.CurrentCulture.EnglishName; 
     s += HttpContext.Current == null ? " No Context" : " Has Context"; 
     Debug.WriteLine(s); 
     return s; 
    } 

Ich mag wäre in der Lage sein, um sicherzustellen, dass die Current fr-FR ist, und dass HttpContext.Current ist an jedem Punkt nicht null wo TestOutput genannt wird. Es ist mir nicht gelungen, das für den "In Task" -Aufruf mit allem zu tun, was ich versucht habe. Auch in einigen meiner Tests ändert sich die Thread-ID nicht, was darauf hindeutet, dass ich die Asynchronität der Methode effektiv entfernt habe. Wie kann ich sicherstellen, dass die culture und HttpContext.Current bei jedem Aufruf von TestOutput beibehalten werden, und dass der Code für verschiedene Threads ausgeführt werden kann?

Capturing HttpContext.Current in einer Schließung und dann einfach wieder zu setzen funktioniert nicht für mich, wie ich Medium Trust unterstützen muss, die eine Sicherheitsausnahme beim Aufruf der HttpContext.Current Setter auslösen wird.

Antwort

6

Der Kontext bleibt erhalten, wenn Sie await Aufgaben.

Was Sie sehen, ist, dass es keinen Zusammenhang für Thread-Pool Aufgaben (Task.Run, TaskFactory.StartNew, oder was das betrifft BackgroundWorker oder Thread oder Delegate.BeginInvoke). Dies ist normal und erwartet.

Verwenden Sie also keine Thread-Pool-Aufgabe. Ihr Beispielcode scheint eine parallele Verarbeitung mit mehreren Threads mit der HttpContext durchzuführen, was einfach nicht möglich ist.

Sie können gleichzeitig async Methoden tun, wenn Sie wollen, aber dies erfordert, dass Ihre Thread.Sleep tatsächlich ein async Verfahren anstelle einer CPU-basierte Methode sein kann:

[HttpGet] 
public async Task<IEnumerable<string>> Test() 
{ 
    Thread.CurrentThread.CurrentCulture = new CultureInfo("fr-FR"); 
    Thread.CurrentThread.CurrentUICulture = Thread.CurrentThread.CurrentCulture; 

    var output = new List<string> { TestOutput("Action start") }; 

    var task = SlowStringAsync(); 
    output.Add(TestOutput("Action Mid")); 
    output.Add(await task); 
    output.Add(TestOutput("Action end")); 
    return output; 
} 

public async Task<string> SlowStringAsync() 
{ 
    await Task.Delay(1000); 
    return TestOutput("In Task"); 
} 

Wenn Ihre alte Bibliothek aus Ihrer Kontrolle und Sie können es nicht async machen, dann müssen Sie es synchron nennen. Es ist akzeptabel, eine synchrone Methode von einem async Verfahren in Situationen wie diese zu nennen:

[HttpGet] 
public async Task<IEnumerable<string>> Test() 
{ 
    Thread.CurrentThread.CurrentCulture = new CultureInfo("fr-FR"); 
    Thread.CurrentThread.CurrentUICulture = Thread.CurrentThread.CurrentCulture; 

    var output = new List<string> { TestOutput("Action start") }; 

    output.Add(TestOutput("Action Mid")); 
    Thread.Sleep(1000); 
    output.Add(TestOutput("Not Really In Task")); 
    output.Add(TestOutput("Action end")); 
    return output; 
} 
+0

Es war mein Verständnis, dass AspNetSynchronizationContext speziell Dinge wie HttpContext beibehalten, wenn auf einem anderen Thread ausgeführt wird. Missverstehe ich oder ist AspNetSynchronizationContext in dieser Situation einfach nicht anwendbar? – ScottS

+0

'AspNetSynchronizationContext' behält' HttpContext' bei, aber nur ein Thread kann sich jeweils in einem Anfragekontext befinden. Sie können den Kontext nicht auf andere Threads setzen, aber "AspNetSynchronizationContext" ist dafür verantwortlich, den Kontext in den Thread-Pool-Thread einzufügen, der die 'async'-Methode wieder aufnimmt, nachdem sie' wartet' hat. –

+0

@StephenCleary - Verzeihen Sie meine Unwissenheit, aber würde "CurrentCulture" über Threads beibehalten, wenn Sie die 'Task.Factory.StartNew()' Methode verwenden - z. 'return await Task.Factory.StartNew (() => {doSomething();};' –

7

Eine wenig beachteten Tatsache, HttpContext.Current beschreibbar ist.

var context = HttpContext.Current; 
var task = Task.Factory.StartNew(() => { 
    HttpContext.Current = context; 
    // You may want to set CultureInformation here too. 

    return TestOutput("In Task"); 
}); 
+0

Danke Simon Es ist eine wenig bekannte Tatsache, jedoch wird es eine Sicherheitsausnahme verursachen, wenn es in der mittleren Vertrauensstellung läuft, die ich wie in der Frage erwähnt unterstützen muss. – ScottS

+0

Es ist auch extrem gefährlich, da Sie mehrere Threads mit der gleichen 'HttpContext.Current' haben, die speziell * nicht * für Multithread-Zugriff ausgelegt ist. –

+2

Ich verstehe, dass es nicht die beste Herangehensweise ist, aber im Hinblick darauf, aufgrund von Threading-Problemen gefährlich zu sein, würde es helfen, eine Sperre zu setzen? Wird die Schließvariable während der Sperre zugewiesen oder wartet sie bis zum Zeitpunkt des Lesens? –

Verwandte Themen