使用IHttpAsyncHandler异步调用WebService

这是基本设置。 我们有一个ASP.Net WebForms应用程序,其页面包含一个需要访问外部Web服务的Flash应用程序。 由于(安全我认为)Flash的限制(不要问我,我根本不是Flash专家),我们无法直接从Flash连接到Web服务。 解决方法是在ASP.Net中创建Flash应用程序将调用的代理,然后调用WebService并将结果转发回Flash应用程序。

WebSite虽然拥有非常高的流量,但问题是,如果Web服务完全挂起,那么ASP.Net请求线程将开始备份,这可能导致严重的线程饥饿。 为了解决这个问题,我决定使用专为此目的而设计的IHttpAsyncHandler 。 在其中,我将使用WebClient异步调用Web服务并将响应转发回来。 关于如何正确使用IHttpAsyncHandler的网上很少有样本,所以我只是想确保我做错了。 我在这里的示例节目中使用我的用法: http : //msdn.microsoft.com/en-us/library/ms227433.aspx

这是我的代码:

internal class AsynchOperation : IAsyncResult { private bool _completed; private Object _state; private AsyncCallback _callback; private readonly HttpContext _context; bool IAsyncResult.IsCompleted { get { return _completed; } } WaitHandle IAsyncResult.AsyncWaitHandle { get { return null; } } Object IAsyncResult.AsyncState { get { return _state; } } bool IAsyncResult.CompletedSynchronously { get { return false; } } public AsynchOperation(AsyncCallback callback, HttpContext context, Object state) { _callback = callback; _context = context; _state = state; _completed = false; } public void StartAsyncWork() { using (var client = new WebClient()) { var url = "url_web_service_url"; client.DownloadDataCompleted += (o, e) => { if (!e.Cancelled && e.Error == null) { _context.Response.ContentType = "text/xml"; _context.Response.OutputStream.Write(e.Result, 0, e.Result.Length); } _completed = true; _callback(this); }; client.DownloadDataAsync(new Uri(url)); } } } public class MyAsyncHandler : IHttpAsyncHandler { public IAsyncResult BeginProcessRequest(HttpContext context, AsyncCallback cb, object extraData) { var asynch = new AsynchOperation(cb, context, extraData); asynch.StartAsyncWork(); return asynch; } public void EndProcessRequest(IAsyncResult result) { } public bool IsReusable { get { return false; } } public void ProcessRequest(HttpContext context) { } } 

现在这一切都有效,我认为它应该做到这一点,但我不是百分百肯定。 另外,创建我自己的IAsyncResult似乎有点矫枉过正,我只是想知道是否有一种方法可以利用从Delegate.BeginInvoke返回的IAsyncResult,或者其他东西。 任何反馈欢迎。 谢谢!!

哇,是的,如果你通过利用任务并行库在.NET 4.0上,你可以使这更容易/更清洁。 核实:

 public class MyAsyncHandler : IHttpAsyncHandler { public IAsyncResult BeginProcessRequest(HttpContext context, AsyncCallback cb, object extraData) { // NOTE: the result of this operation is void, but TCS requires some data type so we just use bool TaskCompletionSource webClientDownloadCompletionSource = new TaskCompletionSource(); WebClient webClient = new WebClient()) HttpContext currentHttpContext = HttpContext.Current; // Setup the download completed event handler client.DownloadDataCompleted += (o, e) => { if(e.Cancelled) { // If it was canceled, signal the TCS is cacnceled // NOTE: probably don't need this since you have nothing canceling the operation anyway webClientDownloadCompletionSource.SetCanceled(); } else if(e.Error != null) { // If there was an exception, signal the TCS with the exception webClientDownloadCompletionSource.SetException(e.Error); } else { // Success, write the response currentHttpContext.Response.ContentType = "text/xml"; currentHttpContext.Response.OutputStream.Write(e.Result, 0, e.Result.Length); // Signal the TCS that were done (we don't actually look at the bool result, but it's needed) taskCompletionSource.SetResult(true); } }; string url = "url_web_service_url"; // Kick off the download immediately client.DownloadDataAsync(new Uri(url)); // Get the TCS's task so that we can append some continuations Task webClientDownloadTask = webClientDownloadCompletionSource.Task; // Always dispose of the client once the work is completed webClientDownloadTask.ContinueWith( _ => { client.Dispose(); }, TaskContinuationOptions.ExecuteSynchronously); // If there was a callback passed in, we need to invoke it after the download work has completed if(cb != null) { webClientDownloadTask.ContinueWith( webClientDownloadAntecedent => { cb(webClientDownloadAntecedent); }, TaskContinuationOptions.ExecuteSynchronously); } // Return the TCS's Task as the IAsyncResult return webClientDownloadTask; } public void EndProcessRequest(IAsyncResult result) { // Unwrap the task and wait on it which will propagate any exceptions that might have occurred ((Task)result).Wait(); } public bool IsReusable { get { return true; // why not return true here? you have no state, it's easily reusable! } } public void ProcessRequest(HttpContext context) { } }