2012-06-29 31 views
5

Tôi đang cố gắng kết hợp AsyncController với việc tiêm phụ thuộc. Ứng dụng MVC được đề cập nhận được gần như tất cả dữ liệu của nó thông qua các cuộc gọi dịch vụ web không đồng bộ. Chúng tôi đang gói công việc không đồng bộ trong Công việc từ TPL và thông báo cho AsyncManager của bộ điều khiển khi các tác vụ này hoàn tất.Thực hiện tác vụ thông qua bối cảnh đồng bộ hóa ASP.NET một cách an toàn và không có AsyncManager.Sync

Đôi khi chúng ta phải chạm vào HttpContext trong việc tiếp tục các tác vụ này - thêm cookie, bất cứ điều gì. Cách chính xác để thực hiện việc này theo số Using an Asynchronous Controller in ASP.NET MVC là gọi phương thức AsyncManager.Sync. Điều này sẽ lan truyền ngữ cảnh thread ASP.NET, bao gồm cả HttpContext, tới luồng hiện tại, thực hiện gọi lại, sau đó khôi phục lại bối cảnh trước đó.

Tuy nhiên, bài viết đó cũng nói:

Calling Sync() từ một chủ đề đó là đã thuộc thẩm quyền của ASP.NET đã không xác định hành vi.

Đây không phải là vấn đề nếu bạn thực hiện tất cả công việc trong bộ điều khiển vì bạn thường biết bạn nên làm gì trong phần tiếp theo. Nhưng những gì tôi đang cố gắng làm là tạo một lớp giữa ở giữa truy cập dữ liệu async và các bộ điều khiển async của chúng ta. Vì vậy, đây cũng là không đồng bộ. Tất cả mọi thứ được kết nối bởi một container DI.

Vì vậy, như trước đây, một số thành phần trong chuỗi cuộc gọi sẽ cần phải làm việc với HttpContext "hiện tại". Ví dụ, sau khi đăng nhập, chúng tôi muốn lưu trữ mã thông báo "phiên" mà chúng tôi nhận được từ dịch vụ đăng nhập một lần. Sự trừu tượng cho điều đó là ISessionStore. Hãy nghĩ đến một CookieSessionStore đặt một cookie lên câu trả lời hoặc lấy cookie từ yêu cầu.

Hai vấn đề tôi có thể thấy với điều này:

  1. Các thành phần không có quyền truy cập vào AsyncManager hoặc thậm chí biết họ đang được sử dụng trong một bộ điều khiển.
  2. Các thành phần không biết họ đang được gọi ra, và vì vậy AsyncManager.Sync hoặc bất kỳ tương đương nào về mặt lý thuyết đều có vấn đề.

Để giải quyết # 1, tôi về cơ bản tiêm một đối tượng mà grabs TaskScheduler.FromCurrentSynchronizationContext() vào đầu yêu cầu, và có thể gọi một hành động thông qua một nhiệm vụ bắt đầu với lịch trình đó, lấy HttpContextBase như một cuộc tranh cãi.

Đó là, từ các thành phần của tôi, tôi có thể gọi một cái gì đó tương tự như:

MySyncObject.Sync(httpContext => /* Add a cookie or something else */); 

Tôi vẫn chưa thấy bất kỳ vấn đề với điều này, nhưng tôi lo ngại về 2 vấn đề #. Tôi đã xem xét cả hai AsyncManagerSynchronizationContextTaskScheduler trong Trình phản xạ và chúng hoạt động tương tự, thực thi cuộc gọi lại trên ASP.NET SynchronizationContext. Và điều đó làm tôi sợ :)

Tôi đã có một chút hy vọng khi tôi thấy rằng việc thực hiện công việc lên lịch sẽ gọi trực tiếp thay vì trải qua bối cảnh đồng bộ hóa nếu nội tuyến của nó. Nhưng thật không may, điều này dường như không xảy ra thông qua đường dẫn mã thông thường Task.Start(scheduler). Thay vào đó, các nhiệm vụ có thể được nêu trong các trường hợp khác, như thể chúng đang được chờ đợi trước khi chúng bắt đầu.

Vì vậy, câu hỏi của tôi là:

  1. Am Tôi sẽ chạy vào rắc rối ở đây với phương pháp này?
  2. Có cách nào tốt hơn không?
  3. Tính hữu dụng của việc đồng bộ hóa trong trường hợp này chỉ đơn thuần là truy cập tuần tự hóa vào HttpContext không an toàn không? tức là tôi có thể lấy đi bằng một gói bọc HttpContextBase an toàn chỉ thay thế (ick)?
+0

Nếu bạn sử dụng 'Task.Start (scheduler)', nó vẫn có thể nó sẽ được inlined, nếu bạn 'Wait()' trên nó đủ nhanh. Tất nhiên, chúng ta đang nói về đa luồng, vì vậy có thể “ngay lập tức” sẽ không “đủ nhanh”. – svick

+0

Hmm Tôi không thấy điều đó. Tất nhiên, tôi đã cài đặt .NET 4.5 ngay bây giờ, vậy ai biết được? :) –

Trả lời

1

Dựa vào thống kê luồng địa phương hiếm khi là ý tưởng hay. Trong khi HttpContext.Current dựa vào cơ chế đó và nó đã hoạt động trong nhiều năm, bây giờ chúng ta sẽ không đồng bộ, cách tiếp cận đó nhanh chóng xấu đi. Tốt hơn hết là nắm bắt giá trị của tĩnh này như là một biến cục bộ và truyền xung quanh đó với công việc không đồng bộ của bạn để bạn luôn có nó. Vì vậy, ví dụ:

public async Task<ActionResult> MyAction() { 
    var context = HttpContext.Current; 
    await Task.Yield(); 
    var item = context.Items["something"]; 
    await Task.Yield(); 
    return new EmptyResult(); 
} 

Hoặc thậm chí tốt hơn, tránh HttpContext.Current hoàn toàn nếu bạn đang ở trong MVC:

public async Task<ActionResult> MyAction() { 
    await Task.Yield(); 
    var item = this.HttpContext.Items["something"]; 
    await Task.Yield(); 
    return new EmptyResult(); 
} 

Có thể cho rằng, logic kinh doanh trung gian của bạn đặc biệt không nên dựa vào HttpContext hoặc bất cứ điều gì khác trong thư viện ASP.NET. Vì vậy, giả sử phần mềm trung gian của bạn gọi lại cho bộ điều khiển của bạn (thông qua callbacks, giao diện, v.v.) để đặt cookie, bạn sẽ có sẵn this.HttpContext để truy cập ngữ cảnh đó.

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