2012-11-26 20 views
6

Tôi đang xây dựng một tập hợp các dịch vụ WebAPI được lưu trữ trên ASP.Net phải sử dụng một thư viện cũ phụ thuộc rất nhiều vào HttpContext.Current. Tôi gặp sự cố khi đảm bảo bối cảnh đó được giữ nguyên trong tất cả các phương thức tham gia vào cuộc gọi không đồng bộ. Tôi đã thử một số biến thể với await/Task.Wait và TaskScheduler.FromCurrentSynchronizationContext() trên đoạn mã dưới đây.Bảo tồn HttpContext khi không đồng bộ với WebAPI (Medium Trust)

[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; 
    } 

Tôi muốn đảm bảo rằng CurrentCulture là fr-FR và HttpContext.Current không phải là null tại mỗi điểm mà TestOutput được gọi. Tôi đã không thành công trong việc làm điều đó cho cuộc gọi "Trong nhiệm vụ" với bất cứ điều gì tôi đã cố gắng. Ngoài ra trong một số id thread thử nghiệm của tôi không bao giờ thay đổi cho thấy rằng tôi đã có hiệu quả loại bỏ asynchronicity của phương pháp. Làm thế nào tôi có thể đảm bảo rằng văn hóa và HttpContext.Current được bảo quản tại mỗi cuộc gọi đến TestOutput, và rằng mã là miễn phí để chạy trên các chủ đề khác nhau?

Chụp HttpContext.Trong quá trình đóng và sau đó chỉ cần thiết lập lại nó sẽ không hoạt động đối với tôi vì tôi cần hỗ trợ Medium Trust, điều này sẽ ném một ngoại lệ bảo mật khi gọi hàm HttpContext.Current setter.

Trả lời

6

Bối cảnh được giữ nguyên bất cứ khi nào bạn await tác vụ.

Những gì bạn đang thấy là không có bối cảnh cho các nhiệm vụ bơi thread (Task.Run, TaskFactory.StartNew, hoặc cho rằng vấn đề BackgroundWorker hoặc Thread hoặc Delegate.BeginInvoke). Điều này là bình thường và được mong đợi.

Vì vậy, không sử dụng tác vụ của nhóm chủ đề. Mã ví dụ của bạn dường như muốn thực hiện xử lý song song với nhiều luồng có số HttpContext, điều này không thể thực hiện được.

Bạn có thể làm đồng thời async phương pháp nếu bạn muốn, nhưng điều này đòi hỏi Thread.Sleep của bạn thực sự có thể là một phương pháp async thay vì một phương pháp dựa trên CPU:

[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"); 
} 

Nếu thư viện cũ của bạn là ngoài tầm kiểm soát của bạn và bạn không thể làm cho nó async, sau đó bạn sẽ phải gọi nó đồng bộ. Đó là chấp nhận được để gọi một phương thức đồng bộ từ một phương pháp async trong những tình huống như thế này:

[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

Đó là sự hiểu biết của tôi rằng AspNetSynchronizationContext là đặc biệt để lưu giữ những thứ như HttpContext khi được thực thi trên một chuỗi khác. Tôi có hiểu lầm, hoặc là AspNetSynchronizationContext không áp dụng trong trường hợp này? – ScottS

+0

'AspNetSynchronizationContext' giữ nguyên' HttpContext', nhưng chỉ một luồng có thể nằm trong ngữ cảnh yêu cầu tại một thời điểm. Bạn không thể đặt bối cảnh trên các chủ đề khác, nhưng 'AspNetSynchronizationContext' phụ trách việc đặt ngữ cảnh vào luồng thread thread để tiếp tục phương thức' async' sau khi nó đang chờ đợi. –

+0

@StephenCleary - Hãy tha thứ cho sự thiếu hiểu biết của tôi nhưng sẽ 'CurrentCulture' được bảo toàn qua các chủ đề nếu sử dụng phương thức' Task.Factory.StartNew() '- ví dụ: 'return await Task.Factory.StartNew (() => {doSomething();};' –

7

Một chút thực tế chú ý, HttpContext.Current là ghi.

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

Cảm ơn Đó là một thực tế ít được biết đến, tuy nhiên nó sẽ gây ra một ngoại lệ bảo mật khi chạy trong Medium Trust mà tôi cần hỗ trợ như đã nêu trong câu hỏi. – ScottS

+0

Nó cũng cực kỳ nguy hiểm vì bạn có nhiều luồng với cùng một 'HttpContext.Current', cụ thể là * không * được thiết kế để truy cập đa luồng. –

+2

Tôi hiểu đó không phải là cách tiếp cận tốt nhất, nhưng liên quan đến việc nguy hiểm do các vấn đề về luồng, liệu việc đặt một khóa quanh nó có giúp ích gì không? Biến đóng cửa có được gán trong khóa hay nó sẽ đợi cho đến khi nó được đọc? –

Các vấn đề liên quan