技术开发 频道

ASP.NET的各种异步操作实现详细解析

       异步HttpModule的实现方式

  在【用Asp.net写自己的服务框架】中,我示范

internal class DirectProcessRequestMoudle : IHttpModule
{
    
public void Init(HttpApplication app)
    {
        app.PostAuthorizeRequest
+= new EventHandler(app_PostAuthorizeRequest);
    }

过如果编写一个HttpModule,通常只要我们实现IHttpModule接口,并在Init方法中订阅一些事件就可以了:

  HttpHandler有异步接口的IHttpAsyncHandler,但HttpModule却只有一个接口:IHttpModule,不管是同步还是异步。异步HttpModule的实现方式并不是订阅HttpApplication的事件,而是调用HttpApplication的一些注册异步操作的方法来实现的(还是在Init事件中),这些方法可参考以下列表:

// 将指定的 System.Web.HttpApplication.AcquireRequestState 事件
// 添加到当前请求的异步 System.Web.HttpApplication.AcquireRequestState事件处理程序的集合。
public void AddOnAcquireRequestStateAsync(BeginEventHandler beginHandler, EndEventHandler endHandler, object state);

// 将指定的 System.Web.HttpApplication.AuthenticateRequest 事件
// 添加到当前请求的异步 System.Web.HttpApplication.AuthenticateRequest事件处理程序的集合。
public void AddOnAuthenticateRequestAsync(BeginEventHandler beginHandler, EndEventHandler endHandler, object state);

// 将指定的 System.Web.HttpApplication.AuthorizeRequest 事件
// 添加到当前请求的异步 System.Web.HttpApplication.AuthorizeRequest事件处理程序的集合。
public void AddOnAuthorizeRequestAsync(BeginEventHandler beginHandler, EndEventHandler endHandler, object state);

// 将指定的 System.Web.HttpApplication.BeginRequest 事件
// 添加到当前请求的异步 System.Web.HttpApplication.BeginRequest事件处理程序的集合。
public void AddOnBeginRequestAsync(BeginEventHandler beginHandler, EndEventHandler endHandler, object state);

// 将指定的 System.Web.HttpApplication.EndRequest 事件
// 添加到当前请求的异步 System.Web.HttpApplication.EndRequest事件处理程序的集合。
public void AddOnEndRequestAsync(BeginEventHandler beginHandler, EndEventHandler endHandler, object state);

public void AddOnLogRequestAsync(BeginEventHandler beginHandler, EndEventHandler endHandler, object state);

public void AddOnMapRequestHandlerAsync(BeginEventHandler beginHandler, EndEventHandler endHandler, object state);

// 将指定的 System.Web.HttpApplication.PostAcquireRequestState 事件
// 添加到当前请求的异步 System.Web.HttpApplication.PostAcquireRequestState事件处理程序的集合。
public void AddOnPostAcquireRequestStateAsync(BeginEventHandler beginHandler, EndEventHandler endHandler, object state);

// 将指定的 System.Web.HttpApplication.PostAuthenticateRequest 事件
// 添加到当前请求的异步 System.Web.HttpApplication.PostAuthenticateRequest事件处理程序的集合。
public void AddOnPostAuthenticateRequestAsync(BeginEventHandler beginHandler, EndEventHandler endHandler, object state);

// 将指定的 System.Web.HttpApplication.PostAuthorizeRequest 事件
// 添加到当前请求的异步 System.Web.HttpApplication.PostAuthorizeRequest事件处理程序的集合。
public void AddOnPostAuthorizeRequestAsync(BeginEventHandler beginHandler, EndEventHandler endHandler, object state);

public void AddOnPostLogRequestAsync(BeginEventHandler beginHandler, EndEventHandler endHandler, object state);

// 将指定的 System.Web.HttpApplication.PostMapRequestHandler 事件
// 添加到当前请求的异步 System.Web.HttpApplication.PostMapRequestHandler事件处理程序的集合。
public void AddOnPostMapRequestHandlerAsync(BeginEventHandler beginHandler, EndEventHandler endHandler, object state);

// 将指定的 System.Web.HttpApplication.PostReleaseRequestState 事件
// 添加到当前请求的异步 System.Web.HttpApplication.PostReleaseRequestState事件处理程序的集合。
public void AddOnPostReleaseRequestStateAsync(BeginEventHandler beginHandler, EndEventHandler endHandler, object state);

// 将指定的 System.Web.HttpApplication.PostRequestHandlerExecute 事件
// 添加到当前请求的异步 System.Web.HttpApplication.PostRequestHandlerExecute事件处理程序的集合。
public void AddOnPostRequestHandlerExecuteAsync(BeginEventHandler beginHandler, EndEventHandler endHandler, object state);

// 将指定的 System.Web.HttpApplication.PostResolveRequestCache 事件
// 添加到当前请求的异步 System.Web.HttpApplication.PostResolveRequestCache事件处理程序的集合。
public void AddOnPostResolveRequestCacheAsync(BeginEventHandler beginHandler, EndEventHandler endHandler, object state);

// 将指定的 System.Web.HttpApplication.PostUpdateRequestCache 事件
// 添加到当前请求的异步 System.Web.HttpApplication.PostUpdateRequestCache事件处理程序的集合。
public void AddOnPostUpdateRequestCacheAsync(BeginEventHandler beginHandler, EndEventHandler endHandler, object state);

// 将指定的 System.Web.HttpApplication.PreRequestHandlerExecute 事件
// 添加到当前请求的异步 System.Web.HttpApplication.PreRequestHandlerExecute事件处理程序的集合。
public void AddOnPreRequestHandlerExecuteAsync(BeginEventHandler beginHandler, EndEventHandler endHandler, object state);

// 将指定的 System.Web.HttpApplication.ReleaseRequestState 事件
// 添加到当前请求的异步 System.Web.HttpApplication.ReleaseRequestState事件处理程序的集合。
public void AddOnReleaseRequestStateAsync(BeginEventHandler beginHandler, EndEventHandler endHandler, object state);

// 将指定的 System.Web.HttpApplication.ResolveRequestCache 事件处理程序
// 添加到当前请求的异步 System.Web.HttpApplication.ResolveRequestCache事件处理程序的集合。
public void AddOnResolveRequestCacheAsync(BeginEventHandler beginHandler, EndEventHandler endHandler, object state);

// 将指定的 System.Web.HttpApplication.UpdateRequestCache 事件
// 添加到当前请求的异步 System.Web.HttpApplication.UpdateRequestCache事件处理程序的集合。
public void AddOnUpdateRequestCacheAsync(BeginEventHandler beginHandler, EndEventHandler endHandler, object state);

   每个方法的含义从它们的名字是可以看出。异步HttpModule的实现方式需要将异步对应的Begin/End二个方法分别做为委托参数传入这些方法中。

  注意:这些方法的签名与Page.AddOnPreRenderCompleteAsync()是一致的,因此它们的具体用法也与Page.AddOnPreRenderCompleteAsync()一样。

  为什么这里不设计成订阅事件的方式?

  我想是因为:如果采用事件模式,调用者可以只订阅其中的一个事件,ASP.NET不容易控制,还有"object state"这个参数不便于在订阅事件时传入。

  异步HttpModule的示例代码如下:

/// <summary>
/// 【示例代码】演示异步的HttpModule
/// 说明:这个示例一丁点意义也没有,纯粹是为了演示。
/// </summary>
public class MyAsyncHttpModule  : IHttpModule
{
    
public static readonly object HttpContextItemsKey = new object();

    
private static readonly string s_QueryDatabaseListScript =
        @
"select dtb.name  from master.sys.databases as dtb order by 1";

    
private static readonly string s_ConnectionString =
        @
"server=localhost\sqlexpress;Integrated Security=SSPI;Asynchronous Processing=true";


    
public void Init(HttpApplication app)
    {
        
// 注册异步事件
        app.AddOnBeginRequestAsync(BeginCall, EndExecuteReader, null);
    }
    
    
private IAsyncResult BeginCall(object sender, EventArgs e, AsyncCallback cb, object extraData)
    {
        SqlConnection connection
= new SqlConnection(s_ConnectionString);
        connection.Open();

        SqlCommand
command = new SqlCommand(s_QueryDatabaseListScript, connection);

        CallbackParam cbParam
= new CallbackParam {
            
Command = command,
            Context
= HttpContext.Current
        };

        
return command.BeginExecuteReader(cb, cbParam);
    }

    
private class CallbackParam
    {
        
public SqlCommand Command;
        
public HttpContext Context;
    }

    
private void EndExecuteReader(IAsyncResult ar)
    {
        CallbackParam cbParam
= (CallbackParam)ar.AsyncState;
        StringBuilder sb
= new StringBuilder();

        
try {
            
using( SqlDataReader reader = cbParam.Command.EndExecuteReader(ar) ) {
                
while( reader.Read() ) {
                    sb.Append(reader.GetString(
0)).Append("; ");
                }
            }
        }
        
catch( Exception ex ) {
            cbParam.Context.Items[HttpContextItemsKey]
= ex.Message;
        }
        
finally {
            cbParam.Command.Connection.Close();
        }

        
if( sb.Length > 0 )
            cbParam.Context.Items[HttpContextItemsKey]
= "数据库列表:" + sb.ToString(0, sb.Length - 2);
    }

    
public void Dispose()
    {
    }
}

   页面可以使用如下方式获得MyAsyncHttpModule的结果:

public partial class TestMyAsyncHttpModule : System.Web.UI.Page
{
    
protected void Page_Load(object sender, EventArgs e)
    {
        
string result = (string)HttpContext.Current.Items[MyAsyncHttpModule.HttpContextItemsKey]
                        ??
"没有开启MyAsyncHttpModule,请在web.config中启用它。";
        Response.Write(result);
    }
}

   说明:管线处理过程中,可能有多个HttpModule,但是异步的HttpModule在执行时,只是在一个阶段内,所有的HttpModule采用异步方式工作。当进入下一个阶段前,必须要等到所有HttpModule全部在当前阶段内执行完毕。

  通常情况下,是没有必要写异步的HttpModule的。这是我写的第一个异步HttpModule。

  异步的 Web Service

  由于Web Service也是受ASP.NET支持,且随着ASP.NET一起出现。我们再来看一下如果将一个同步的服务方法改变成异步的方法。

  注意:将方法由同步改成异步版本,是不影响客户端的。

  以下代码是一个同步版本的服务方法:

[WebMethod]
public string ExtractNumber(string str)
{
    
//return ........
}

   再来看一下最终的异步实现版本:

[WebMethod]
public IAsyncResult BeginExtractNumber(string str, AsyncCallback cb, object state)
{
    MyHttpClient
<string, string> http = new MyHttpClient<string, string>();
    http.UserData
= "Begin ThreadId: " + Thread.CurrentThread.ManagedThreadId.ToString();

    
return http.BeginSendHttpRequest(ServiceUrl, str, cb, http);
}

[WebMethod]
public string EndExtractNumber(IAsyncResult ar)
{
    MyHttpClient
<string, string> http = (MyHttpClient<string, string>)ar.AsyncState;
    
try{
        
return http.EndSendHttpRequest(ar) +
            
", " + http.UserData.ToString() +
            
", End ThreadId: " + Thread.CurrentThread.ManagedThreadId.ToString();
    }
    
catch(Exception ex){
        
return ex.ToString();
    }
}

   其实,要做的修改与IHttpHandler到IHttpAsyncHandler的工作差不多,在原有的同步方法后面加二个与异步操作有关的参数,并且返回值改为IAsyncResult,然后再添加一个EndXxxx方法就可以了,当然了,EndXxxx方法的传入参数只能是一个IAsyncResult类型的参数。

0
相关文章