2014-07-03 17 views
19

Cố gắng truy cập HttpContext.Current trong cuộc gọi phương thức để tôi có thể sửa đổi biến số Session, tuy nhiên tôi nhận được ngoại lệ HttpContext.Currentnull. Phương thức gọi lại được kích hoạt không đồng bộ, khi đối tượng _anAgent kích hoạt nó.HttpContext.Current is null trong Gọi lại không đồng bộ

Tôi vẫn không chắc chắn về giải pháp này sau khi xem similarquestions trên SO.

Một phiên bản đơn giản hóa của mã của tôi trông giống như vậy:

public partial class Index : System.Web.UI.Page 

    protected void Page_Load() 
    { 
    // aCallback is an Action<string>, triggered when a callback is received 
    _anAgent = new WorkAgent(..., 
          aCallback: Callback); 
    ... 
    HttpContext.Current.Session["str_var"] = _someStrVariable; 
    } 

    protected void SendData() // Called on button click 
    { 
    ... 
    var some_str_variable = HttpContext.Current.Session["str_var"]; 

    // The agent sends a message to another server and waits for a call back 
    // which triggers a method, asynchronously. 
    _anAgent.DispatchMessage(some_str_variable, some_string_event) 
    } 

    // This method is triggered by the _webAgent 
    protected void Callback(string aStr) 
    { 
    // ** This culprit throws the null exception ** 
    HttpContext.Current.Session["str_var"] = aStr; 
    } 

    [WebMethod(EnableSession = true)] 
    public static string GetSessionVar() 
    { 
    return HttpContext.Current.Session["str_var"] 
    } 
} 

Không chắc nếu cần thiết nhưng lớp WorkAgent của tôi trông giống như vậy:

public class WorkAgent 
{ 
    public Action<string> OnCallbackReceived { get; private set; } 

    public WorkAgent(..., 
        Action<string> aCallback = null) 
    { 
    ... 
    OnCallbackReceived = aCallback; 
    } 

    ... 

    // This method is triggered when a response is received from another server 
    public BackendReceived(...) 
    { 
    ... 
    OnCallbackReceived(some_string); 
    } 
} 

gì sẽ xảy ra trong các mã:
Nhấp vào nút gọi phương thức SendData(), bên trong này _webAgent sẽ gửi thư đến máy chủ khác và chờ trả lời (trong khi đó người dùng vẫn có thể tương tác với trang này và tham khảo cùng một số SessionID). Sau khi nhận được, nó gọi phương thức BackendReceived(). Quay lại trang .aspx.cs gọi phương thức Callback().

Câu hỏi:
Khi WorkAgent gây nên các phương pháp Callback() nó cố gắng truy cập HttpContext.Current đó là null. Tại sao trường hợp nếu tôi tiếp tục, bỏ qua ngoại lệ, tôi vẫn có thể nhận được cùng một số SessionID và biến số Session bằng cách sử dụng phương thức ajax trả về phương thức GetSessionVar().

Tôi có nên bật cài đặt aspNetCompatibilityEnabled không?
Tôi có nên tạo một số loại asynchronous module handler không?
Điều này có liên quan đến Integrated/Classic mode không?

+0

Tại sao phức tạp này bằng cách sử dụng callbacks, một giải pháp tốt hơn có thể được sử dụng ajax từ phía khách hàng, như vậy người dùng vẫn có thể tương tác với trang web. Và cuộc gọi đến hệ thống khác chỉ có thể là một phương thức bình thường gọi là – 3dd

+0

Ajax * được * sử dụng từ phía máy khách cho hầu hết các phần, chỉ cần không bao gồm nó trong mã trên (nó cập nhật các biến 'HttpContext Session' và cơ sở dữ liệu SQL) . Phương thức duy nhất không phải là một cuộc gọi ajax là 'SendData()'. Điều này sẽ gửi dữ liệu đến * một số máy chủ * khác. Tôi chỉ nhầm lẫn tại sao 'HttpContect.Current' trở thành ** null ** trên gọi lại. –

+0

Vui lòng xem câu trả lời của tôi để được giải thích lý do tại sao điều này xảy ra – 3dd

Trả lời

3

Xin vui lòng xem các bài viết sau đây cho một lời giải thích về việc tại sao các biến Session là null, và xung quanh có

http://adventuresdotnet.blogspot.com/2010/10/httpcontextcurrent-and-threads-with.html

trích dẫn từ các từ các bài báo;

dòng HttpContext thực sự là trong lưu trữ thread-địa phương, điều này giải thích lý do tại sao chủ đề con không có quyền truy cập vào nó

Và như một tác phẩm đề xuất xung quanh tác giả nói

chuyển một tham chiếu đến nó trong chủ đề con của bạn. Bao gồm một tham chiếu đến HttpContext trong “nhà nước” đối tượng của phương pháp gọi lại của bạn, và sau đó bạn có thể lưu nó để HttpContext.Current về chủ đề đó

3

Khi sử dụng chủ đề hoặc một async chức năng, HttpContext.Current không có sẵn.

Hãy thử sử dụng:

HttpContext current; 
if(HttpContext != null && HttpContext.Current != null) 
{ 
    current = HttpContext.Current; 
} 
else 
{ 
    current = this.CurrentContext; 
    //**OR** current = threadInstance.CurrentContext; 
} 

Khi bạn thiết lập current với một trường hợp thích hợp, phần còn lại của mã của bạn là độc lập, cho dù gọi từ một sợi hoặc trực tiếp từ một WebRequest.

6

Đây là giải pháp dựa trên lớp đang hoạt động cho các trường hợp đơn giản cho đến nay trong MVC5 (MVC6 hỗ trợ ngữ cảnh dựa trên DI).

using System.Threading; 
using System.Web; 

namespace SomeNamespace.Server.ServerCommon.Utility 
{ 
    /// <summary> 
    /// Preserve HttpContext.Current across async/await calls. 
    /// Usage: Set it at beginning of request and clear at end of request. 
    /// </summary> 
    static public class HttpContextProvider 
    { 
     /// <summary> 
     /// Property to help ensure a non-null HttpContext.Current. 
     /// Accessing the property will also set the original HttpContext.Current if it was null. 
     /// </summary> 
     static public HttpContext Current => HttpContext.Current ?? (HttpContext.Current = __httpContextAsyncLocal?.Value); 

     /// <summary> 
     /// MVC5 does not preserve HttpContext across async/await calls. This can be used as a fallback when it is null. 
     /// It is initialzed/cleared within BeginRequest()/EndRequest() 
     /// MVC6 may have resolved this issue since constructor DI can pass in an HttpContextAccessor. 
     /// </summary> 
     static private AsyncLocal<HttpContext> __httpContextAsyncLocal = new AsyncLocal<HttpContext>(); 

     /// <summary> 
     /// Make the current HttpContext.Current available across async/await boundaries. 
     /// </summary> 
     static public void OnBeginRequest() 
     { 
      __httpContextAsyncLocal.Value = HttpContext.Current; 
     } 

     /// <summary> 
     /// Stops referencing the current httpcontext 
     /// </summary> 
     static public void OnEndRequest() 
     { 
      __httpContextAsyncLocal.Value = null; 
     } 
    } 
} 

Để sử dụng nó có thể treo từ Global.asax.cs:

public MvcApplication() // constructor 
    {    
     PreRequestHandlerExecute += new EventHandler(OnPreRequestHandlerExecute); 
     EndRequest += new EventHandler(OnEndRequest); 
    } 

    protected void OnPreRequestHandlerExecute(object sender, EventArgs e) 
    { 
     HttpContextProvider.OnBeginRequest(); // preserves HttpContext.Current for use across async/await boundaries.    
    } 

    protected void OnEndRequest(object sender, EventArgs e) 
    { 
     HttpContextProvider.OnEndRequest(); 
    } 

Sau đó có thể sử dụng ở vị trí của HttpContext.Current:

HttpContextProvider.Current 

Có thể có các vấn đề như Tôi hiện không hiểu điều này related answer. Hãy bình luận.

tham khảo: AsyncLocal (yêu cầu .NET 4.6)

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