自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

為ASP.NET MVC擴(kuò)展異步Action功能(下)

開發(fā) 后端
異步請(qǐng)求處理是ASP.NET 2.0中引入的高級(jí)特性,它依托IO Complete Port,對(duì)于提高IO密集型應(yīng)用程序的吞吐量非常重要。但是目前ASP.NET MVC框架缺少異步Action功能。本文作者給出了不錯(cuò)的擴(kuò)展:完整,方便,并且非常輕巧——核心邏輯代碼只有200行左右,這意味著絕大部分功能將會(huì)委托給框架中現(xiàn)成的內(nèi)容,確保了擴(kuò)展的穩(wěn)定,高效并且擁有較好的向后兼容性。

編輯推薦:為ASP.NET MVC擴(kuò)展異步Action功能(上)

執(zhí)行Action方法

對(duì)于執(zhí)行同步Action的SyncMvcHandler,其實(shí)現(xiàn)十分簡(jiǎn)單而直接:

public class SyncMvcHandler : IHttpHandler, IRequiresSessionState
{
    public SyncMvcHandler(
        IController controller,
        IControllerFactory controllerFactory,
        RequestContext requestContext)
    {
        this.Controller = controller;
        this.ControllerFactory = controllerFactory;
        this.RequestContext = requestContext;
    }

 

    public IController Controller { get; private set; }
    public RequestContext RequestContext { get; private set; }
    public IControllerFactory ControllerFactory { get; private set; }

 

    public virtual bool IsReusable { get { return false; } }

    public virtual void ProcessRequest(HttpContext context)
    {
        try
        {
            this.Controller.Execute(this.RequestContext);
        }
        finally
        {
            this.ControllerFactory.ReleaseController(this.Controller);
        }
    }
}

而對(duì)于異步Action,我之前一直思考著怎么將框架的默認(rèn)實(shí)現(xiàn),也就是單個(gè)方法調(diào)用,轉(zhuǎn)化成兩個(gè)方法(BeginXxx/EndXxx)調(diào)用。曾經(jīng)我想過(guò)自己實(shí)現(xiàn)一個(gè)新的ActionInvoker,但是這就涉及到了大量的工作,尤其是如果希望保持框架現(xiàn)有的功能(ActionFilter,ActionSelector等等),最省力的方法可能就是繼承ControllerActionInvoker,并設(shè)法使用框架已經(jīng)實(shí)現(xiàn)的各種輔助方法。但是在分析了框架代碼之后我發(fā)現(xiàn)復(fù)用也非常困難,舉例來(lái)說(shuō),ControllerActionInvoker判定一個(gè)方法為Action的依據(jù)之一是這個(gè)方法返回的是ActionResult類型或其子類,這意味著我無(wú)法直接使用這個(gè)方法來(lái)獲取一個(gè)返回IAsyncResult的BeginXxx方法;同理,對(duì)于查找EndXxx方法,我可能需要在請(qǐng)求名為Abc的異步Action時(shí),將EndAbc作為查找依據(jù)交由現(xiàn)成的方法來(lái)查詢——但是,如果又有一個(gè)請(qǐng)求是直接針對(duì)一個(gè)名為EndAbc的同步Action的那又怎么辦呢?

由于這些問(wèn)題存在,我在去年設(shè)法實(shí)現(xiàn)異步Action時(shí)幾乎重寫了整個(gè)ActionInvoker——其復(fù)雜程度可見(jiàn)一斑。而且那個(gè)實(shí)現(xiàn)對(duì)于一些特殊情況的處理依舊不甚友好,需要開發(fā)人員在一定程度上做出妥協(xié)。這個(gè)實(shí)現(xiàn)在TechED 2008 China的Session中公布時(shí)我就承認(rèn)它并不能讓我滿意,建議大家不要將其投入生產(chǎn)環(huán)境中。而現(xiàn)在的實(shí)現(xiàn),則非常順利地解決了整個(gè)問(wèn)題。雖然從理論上講還不夠“完美”,雖然還做出了一些讓步。

帶來(lái)如此多問(wèn)題的原因就在于我們?cè)谠O(shè)法顛覆框架內(nèi)部的關(guān)鍵性設(shè)計(jì),也就是從單一的Action方法調(diào)用,轉(zhuǎn)變?yōu)椤胺螦PM的”二段式調(diào)用。等等,您是否感覺(jué)到了解決問(wèn)題的關(guān)鍵?沒(méi)錯(cuò),那就是“符合APM的”。APM要求我們將一個(gè)行為分為BeginXxx和EndXxx兩個(gè)方法,可是既然ASP.NET MVC框架只能讓我們返回一個(gè)ActionResult對(duì)象……那么我們?yōu)槭裁床辉谶@個(gè)對(duì)象里包含方法的引用——也就是一個(gè)委托對(duì)象呢?這雖然不符合正統(tǒng)的APM簽名,但是完全可行,不是嗎?

public class AsyncActionResult : ActionResult
{
    public AsyncActionResult(
        IAsyncResult asyncResult,
        Func<IAsyncResult, ActionResult> endDelegate)
    {
        this.AsyncResult = asyncResult;
        this.EndDelegate = endDelegate;
    }

    public IAsyncResult AsyncResult { get; private set; }

    public Func<IAsyncResult, ActionResult> EndDelegate { get; private set; }

    public override void ExecuteResult(ControllerContext context)
    {
        context.Controller
            .SetAsyncResult(this.AsyncResult)
            .SetAsyncEndDelegate(this.EndDelegate);
    }
}

由于在Action方法中可以調(diào)用BeginXxx方法,我們?cè)贏syncActionResult中只需保留Begin方法返回的IAsyncResult,以及另一個(gè)對(duì)于EndXxx方法的引用。在AsyncActionResult的ExecuteResult方法中將會(huì)保存這兩個(gè)對(duì)象,以便在AsyncMvcHandler的EndProcessRequest方法中重新獲取并使用。根據(jù)“慣例”,我們還需要定義一個(gè)擴(kuò)展方法,方便開發(fā)人員在Action方法中返回一個(gè)AsyncActionResult。具體實(shí)現(xiàn)非常容易,在這里就展示一下異步Action的編寫方式:

[AsyncAction]
public ActionResult AsyncAction(AsyncCallback asyncCallback, object asyncState)
{
    SqlConnection conn = new SqlConnection("...;Asynchronous Processing=true");
    SqlCommand cmd = new SqlCommand("WAITFOR DELAY '00:00:03';", conn);
    conn.Open();

    return this.Async(
        cmd.BeginExecuteNonQuery(asyncCallback, asyncState),
        (ar) =>
        {
            int value = cmd.EndExecuteNonQuery(ar);
            conn.Close();
            return this.View();
        });
}

至此,似乎AsyncMvcHandler也無(wú)甚秘密可言了:

public class AsyncMvcHandler : IHttpAsyncHandler, IRequiresSessionState
{
    public AsyncMvcHandler(
        Controller controller,
        IControllerFactory controllerFactory,
        RequestContext requestContext)
    {
        this.Controller = controller;
        this.ControllerFactory = controllerFactory;
        this.RequestContext = requestContext;
    }

    public Controller Controller { get; private set; }
    public RequestContext RequestContext { get; private set; }
    public IControllerFactory ControllerFactory { get; private set; }
    public HttpContext Context { get; private set; }

    public IAsyncResult BeginProcessRequest(
        HttpContext context,
        AsyncCallback cb,
        object extraData)
    {
        this.Context = context;
        this.Controller.SetAsyncCallback(cb).SetAsyncState(extraData);

        try
        {
            (this.Controller as IController).Execute(this.RequestContext);
            return this.Controller.GetAsyncResult();
        }
        catch
        {
            this.ControllerFactory.ReleaseController(this.Controller);
            throw;
        }
    }

    public void EndProcessRequest(IAsyncResult result)
    {
        try
        {
            HttpContext.Current = this.Context;
            ActionResult actionResult = this.Controller.GetAsyncEndDelegate()(result);
            if (actionResult != null)
            {
                actionResult.ExecuteResult(this.Controller.ControllerContext);
            }
        }
        finally
        {
            this.ControllerFactory.ReleaseController(this.Controller);
        }
    }
}

在BeginProcessRequest方法中將保存當(dāng)前Context——這點(diǎn)很重要,HttpContext.Current是基于CallContext的,一旦經(jīng)過(guò)一次異步回調(diào)HttpContext.Current就變成了null,我們必須重設(shè)。接著將接收到的AsyncCallback和AsyncState保留,并使用框架中現(xiàn)成的Execute方法執(zhí)行控制器。當(dāng)Execute方法返回時(shí)一整個(gè)Action方法的調(diào)用流程已經(jīng)結(jié)束,這意味著其調(diào)用結(jié)果——即IAsyncResult和EndDelegate對(duì)象已經(jīng)保留。于是將IAsyncResult對(duì)象取出并返回。至于EndProcessRequest方法,只是將BeginProcessRequest方法中保存下來(lái)的EndDelegate取出,調(diào)用,把得到的ActionResult再執(zhí)行一遍即可。

以上的代碼只涉及到普通情況下的邏輯,而在完整的代碼中還會(huì)包括對(duì)于Action方法被某個(gè)Filter終止或替換等特殊情況下的處理。此外,無(wú)論在BeginProcessRequest還是EndProcessRequest中都需要對(duì)異常進(jìn)行合適地處理,使得Controller Factory能夠及時(shí)地對(duì)Controller對(duì)象進(jìn)行釋放。

#p#

ModelBinder支持

其實(shí)您到目前為止還不能使用異步Action,因?yàn)槟鷷?huì)發(fā)現(xiàn)方法的AsyncCallback參數(shù)得到的永遠(yuǎn)是null。這是因?yàn)槟J(rèn)的Model Binder無(wú)法得知如何從一個(gè)上下文環(huán)境中得到一個(gè)AsyncCallback對(duì)象。這一點(diǎn)倒非常簡(jiǎn)單,我們只需要構(gòu)造一個(gè)AsyncCallbackModelBinder,而它的BindModel方法僅僅是將AsyncMvcHandler.BeginProcessRequest方法中保存的AsyncCallback對(duì)象取出并返回:

public sealed class AsyncCallbackModelBinder : IModelBinder
{
    public object BindModel(
        ControllerContext controllerContext,
        ModelBindingContext bindingContext)
    {
        return controllerContext.Controller.GetAsyncCallback();
    }
}

其使用方式,便是在應(yīng)用程序啟動(dòng)時(shí)將其注冊(cè)為AsyncCallback類型的默認(rèn)Binder:

protected void Application_Start()
{
    RegisterRoutes(RouteTable.Routes);
    ModelBinders.Binders[typeof(AsyncCallback)] = new AsyncCallbackModelBinder();
}

對(duì)于asyncState參數(shù)您也可以使用類似的做法,不過(guò)這似乎有些不妥,因?yàn)閛bject類型實(shí)在過(guò)于寬泛,并不能明確代指asyncState參數(shù)。事實(shí)上,即使您不為asyncState設(shè)置binder也沒(méi)有太大問(wèn)題,因?yàn)閷?duì)于一個(gè)異步ASP.NET請(qǐng)求來(lái)說(shuō),其asyncState永遠(yuǎn)是null。如果您一定要指定一個(gè)binder,我建議您在每個(gè)Action方法的asyncState參數(shù)上標(biāo)記如下的Attribute,它和AsyncStateModelBinder也已經(jīng)被一并建入項(xiàng)目中了:

[AttributeUsage(AttributeTargets.Parameter, AllowMultiple = false, Inherited = false)]
public sealed class AsyncStateAttribute : CustomModelBinderAttribute
{
    private static AsyncStateModelBinder s_modelBinder = new AsyncStateModelBinder();
    public override IModelBinder GetBinder()
    {
        return s_modelBinder;
    }
}

使用方式如下:

[AsyncAction]
public ActionResult AsyncAction(AsyncCallback cb, [AsyncState]object state) { ... }

其實(shí),基于Controller的擴(kuò)展方法GetAsyncCallback和GetAsyncState均為公有方法,您也可以讓Action方法不接受這兩個(gè)參數(shù)而直接從Controller中獲取——當(dāng)然這種做法降低了可測(cè)試性,不值得提倡。

限制和缺點(diǎn)

如果這個(gè)解決方案沒(méi)有缺陷,那么相信它已經(jīng)被放入ASP.NET MVC 1.0中,而輪不到我在這里擴(kuò)展一番了。目前的這個(gè)解決方案至少有以下幾點(diǎn)不足:

1. 沒(méi)有嚴(yán)格遵守.NET中的APM模式,雖然不影響功能,但這始終是一個(gè)遺憾。

2. 由于利用了框架中的現(xiàn)成功能,所有的Filter只能運(yùn)行在BeginXxx方法上。

3. 由于EndXxx方法和最終ActionResult的執(zhí)行都沒(méi)有Filter支持,因此如果在這個(gè)過(guò)程中拋出了異常,將無(wú)法進(jìn)入ASP.NET MVC建議的異常處理功能中。

根據(jù)ASP.NET MVC框架的Roadmap,ASP.NET MVC框架1.0之后的版本中將會(huì)支持異步Action,相信以上這些缺陷到時(shí)候都能被彌補(bǔ)。不過(guò)這就需要大量的工作,這只能交給ASP.NET MVC團(tuán)隊(duì)去慢慢執(zhí)行了。事實(shí)上,您現(xiàn)在已經(jīng)可以在ASP.NET MVC RC源代碼的MvcFutures項(xiàng)目中找到異步Action處理的相關(guān)內(nèi)容。它添加了IAsyncController,AsyncController,IAsyncActionInvoker,AsyncControllerActionInvoker等許多擴(kuò)展。雖說(shuō)它們都“繼承”了現(xiàn)有的類,但是與我之前的判斷相似,如AsyncControllerActionInvoker幾乎完全重新實(shí)現(xiàn)了一遍ActionInvoker中的各種功能——我還沒(méi)有仔細(xì)閱讀代碼,因此無(wú)法判斷出這種設(shè)計(jì)是否優(yōu)秀,只希望它能像ASP.NET MVC本身那樣的簡(jiǎn)單和優(yōu)雅。

【編輯推薦】

  1. 為ASP.NET MVC擴(kuò)展異步Action功能(上)
  2. 詳解ASP.NET MVC的請(qǐng)求生命周期
  3. ASP.NET MVC實(shí)例和新RC版本中視圖方面的改進(jìn)
  4. ASP.NET MVC框架視頻教程
責(zé)任編輯:楊鵬飛 來(lái)源: 博客園
相關(guān)推薦

2009-02-16 10:05:11

ActionMVCASP.NET

2009-07-22 10:13:31

異步ActionASP.NET MVC

2009-03-06 10:28:30

MVCASP.NET異步Action

2009-07-22 09:11:02

Action方法ASP.NET MVC

2009-07-22 16:02:39

ASP.NET MVCPagedList

2009-07-24 13:20:44

MVC框架ASP.NET

2009-07-31 12:43:59

ASP.NET MVC

2009-07-20 12:59:53

ASP.NET MVCASP.NET框架的功

2009-07-22 18:07:55

論壇應(yīng)用程序ASP.NET MVC

2010-04-06 15:20:56

ASP.NET MVC

2010-02-05 08:32:32

ASP.NET MVC

2009-07-23 15:44:39

ASP.NET MVC

2009-07-22 10:09:59

ASP.NET MVC

2009-07-22 13:24:24

ASP.NET MVC

2009-07-20 10:53:59

ASP.NET MVC

2009-07-23 14:31:20

ASP.NET MVC

2017-03-06 11:13:57

ASP.NETCoreMVC

2009-07-28 16:40:11

ASP.NET異步頁(yè)面

2015-06-17 17:01:48

ASP.NET

2021-03-08 07:32:05

Actionweb框架
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)