ASP입니다.NET MVC 비동기 Action 기능 확장(하)
Action 메서드 실행
동기화 Action을 실행하는 SyncMvcHandler의 구현은 간단하면서도 직접적입니다.
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);
}
}
}
비동기적인 Action에 대해 저는 프레임워크의 기본적인 실현, 즉 하나의 방법을 호출하여 두 가지 방법(BeginXxx/EndXxx)으로 호출하는 방법을 생각해 왔습니다.일찍이 나는 새로운 Action Invoker를 실현하고 싶다고 생각했지만 이것은 대량의 업무와 관련된다. 특히 프레임워크의 기존 기능(Action Filter,Action Selector 등)을 유지하고 싶다면 가장 효율적인 방법은 Controller Action Invoker를 계승하고 프레임워크가 이미 실현된 각종 보조 방법을 사용하는 것이다.그러나 프레임워크 코드를 분석한 후에 복용도 매우 어렵다는 것을 발견했다. 예를 들어 Controller Action Invoker가 한 방법이 Action이라고 판정한 근거 중 하나는 이 방법이 ActionResult 유형이나 그 하위 클래스로 되돌아오는 것이다. 이것은 내가 이 방법을 직접 사용해서IAsyncResult로 되돌아오는 BeginXxx 방법을 얻을 수 없다는 것을 의미한다.마찬가지로, EndXxx를 찾는 방법에 대해, 나는 Abc라는 비동기적인 Action을 요청할 때, EndAbc를 검색 근거로 기존의 방법에 의해 조회해야 할 수도 있다. 그러나, 만약 또 하나의 요청이 EndAbc라는 동기화 Action을 대상으로 하는 것이라면 어떻게 하겠는가?
이러한 문제들이 존재하기 때문에, 내가 작년에 비동기적인 Action을 실현하려고 했을 때, 거의 모든 Action Invoker를 다시 썼다. 그 복잡성을 알 수 있다.그리고 그 실현은 일부 특수한 상황에 대한 처리가 여전히 우호적이지 않기 때문에 개발자가 어느 정도 타협을 해야 한다.이 실현이 TechED 2008 China의 Session에서 발표되었을 때 저는 그것이 만족스럽지 않다는 것을 인정했고 생산 환경에 투입하지 말라고 건의했습니다.현재의 실현은 전체 문제를 순조롭게 해결했다.이론적으로는 아직 완벽하지는 않지만, 약간의 양보도 했다.
이렇게 많은 문제를 가져온 원인은 우리가 프레임 내부의 관건적인 디자인, 즉 단일한 Action 방법에서'APM에 부합되는'2단식 호출로 전환하려고 하는 데 있다.잠깐만, 문제 해결의 관건을 느꼈습니까?맞아요. 바로 "APM에 맞는 것"입니다.APM은 하나의 행위를 BeginXxx와 EndXxx 두 가지 방법으로 나누도록 요구하지만, 기왕 ASP가NET MVC 프레임워크는ActionResult 대상을 되돌려줄 수 밖에 없습니다. 그렇다면 우리는 왜 이 대상에 방법의 인용, 즉 의뢰 대상을 포함하지 않습니까?이것은 비록 정통 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 방법에서 BeginXxx 방법을 호출할 수 있기 때문에, AsyncActionResult에서는 Begin 방법이 되돌아오는 IasyncResult와 EndXxx 방법에 대한 다른 인용만 보류합니다.AsyncActionResult의 ExecuteResult 방법에는 AsyncMvcHandler의 EndProcessRequest 방법에서 다시 가져와 사용할 수 있도록 두 대상이 저장됩니다.'관례' 에 따라 개발자가 Action 방법에서 AsyncActionResult를 되돌릴 수 있도록 확장 방법을 정의해야 한다.이 슬라이드에서는 비동기식 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도 더 이상 비밀이 없는 것 같다.
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 방법에서 현재 Context를 저장합니다. 이것은 매우 중요합니다. HttpContext.Current는 CallContext 기반이며 HttpContext를 비동기적으로 리셋한 경우Current가 null이 되므로 재설정해야 합니다.이어서 받은 AsyncCallback과 AsyncState를 보존하고 프레임워크에 있는 기존의 Execute 방법으로 컨트롤러를 실행합니다.Execute 방법이 되돌아올 때 전체 Action 방법의 호출 절차가 끝났습니다. 이것은 호출 결과인 IAsyncResult와 EndDelegate 대상이 보류되었다는 것을 의미합니다.IasyncResult 객체를 체크 아웃하고 로 돌아갑니다.EndProcess Request 방법은 BeginProcess Request 방법에 저장된 EndDelegate를 꺼내서 호출하고 얻은ActionResult를 다시 실행하면 됩니다.
이상의 코드는 일반적인 상황에서의 논리에만 관련되고, 전체 코드에는 Action 방법이 어떤 Filter에 의해 종료되거나 교체되는 등 특수한 상황에서의 처리도 포함된다.또한 BeginProcessRequest에서든 EndProcessRequest에서든 이상을 적절하게 처리하여 ControllerFactory가 제때에 Controller 대상을 방출할 수 있도록 해야 한다.
ModelBinder 지원
지금까지 비동기 Action을 사용할 수 없습니다. 방법의 AsyncCallback 매개 변수가 영원히null인 것을 발견할 수 있기 때문입니다.기본 Model Binder에서는 컨텍스트 환경에서 AsyncCallback 객체를 가져오는 방법을 알 수 없기 때문입니다.이 점은 매우 간단하다. 우리는 Async Callback Model Binder를 구축하기만 하면 되고, 그 Bind Model 방법은 단지 Async MvcHandler를 구축하는 것이다.BeginProcessRequest 메서드에 저장된 AsyncCallback 객체를 체크 아웃하고 반환하려면 다음과 같이 하십시오.
public sealed class AsyncCallbackModelBinder : IModelBinder
{
public object BindModel(
ControllerContext controllerContext,
ModelBindingContext bindingContext)
{
return controllerContext.Controller.GetAsyncCallback();
}
}
응용 프로그램이 시작될 때 AsyncCallback 유형의 기본 Binder로 등록하는 방법을 사용합니다.
protected void Application_Start()
{
RegisterRoutes(RouteTable.Routes);
ModelBinders.Binders[typeof(AsyncCallback)] = new AsyncCallbackModelBinder();
}
asyncState 매개 변수에 대해서도 비슷한 방법을 사용할 수 있지만, 이것은 타당하지 않은 것 같습니다. object 형식이 너무 광범위해서 asyncState 매개 변수를 명확하게 가리킬 수 없습니다.사실, asyncState에 binder를 설정하지 않아도 큰 문제가 없습니다. 비동기적인 ASP에 대해서는.NET 요청에 따르면 ASyncState는 항상 null입니다.만약 binder를 지정해야 한다면, 모든 Action 방법의 asyncState 매개 변수에 다음과 같은 Attribute를 표시하는 것을 권장합니다. 이것은 AsyncStateModelBinder와 프로젝트에 함께 만들어졌습니다.
[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) { ... }
사실 Controller 기반의 확장 방법인 GetAsyncCallback과 GetAsyncState는 모두 공유된 방법입니다. 이 두 가지 파라미터를 받아들이지 않고 직접 Controller에서 얻을 수도 있습니다. 물론 이런 방법은 테스트 가능성을 낮추고 제창할 가치가 없습니다.
제한과 단점
만약 이 해결 방안에 결함이 없다면, 그것은 이미 ASP에 넣었을 것이다.NET MVC 1.0에서 내가 여기서 한 번 확장할 차례가 되지 않았다.현재의 이 솔루션은 적어도 다음과 같은 몇 가지 부족한 점이 있습니다.
ASP를 기준으로 합니다.NET MVC 프레임의 Roadmap, ASP.NET MVC 프레임워크 1.0 이후 버전에서는 비동기 Action이 지원될 예정이며 이러한 결함은 그때 보완될 것으로 믿습니다.하지만 많은 일이 필요하기 때문에 ASP에 맡길 수밖에 없어요.NET MVC팀은 서서히 집행하겠습니다.사실 당신은 이미 ASP에서NET MVC RC 소스 코드의 MvcFutures 프로젝트에서 비동기 Action 처리와 관련된 내용을 찾습니다.IAsyncController, AsyncController, IAsyncActionInvoker, AsyncControllerActionInvoker 등 많은 확장자를 추가했습니다.비록 그들은 모두 기존의 유형을'계승'했지만 이전의 판단과 비슷하다. 예를 들어 AsyncController Action Invoker는 Action Invoker의 각종 기능을 거의 완전히 다시 한 번 실현했다. 나는 코드를 자세히 읽지 않았기 때문에 이런 디자인이 우수한지 판단할 수 없다. 단지 ASP와 같기를 바랄 뿐이다.NET MVC 그 자체만큼 심플하고 우아하다.
다음에, 나는 현재 코드의 EndXxx 방법에Filter 지원을 추가할 계획이다. 나는 ASP를 자세히 읽어야 한다.NET MVC의 소스 코드로 솔루션을 찾습니다.ASP가 됐으면 좋겠어요.NET MVC는 비동기식 Action 이전에 좋았던 대안을 공식적으로 지원합니다.
추가 자료
MSDN Code Gallery에 전체 프로젝트 코드가 배치되어 있으며 성능 테스트 등에 대한 자세한 내용은 여기에서 확인할 수 있습니다.이 글은 확장된 디자인 원리를 중점적으로 설명하고 특수 상황 처리와 프로그램의 건장성 등 세부 사항을 실현하는 설명을 생략하였으며 코드를 다운로드하고 개선 제안을 해 주시기 바랍니다.
전편: ASP.NET MVC 비동기식 Action 기능 확장(상)
이 내용에 흥미가 있습니까?
현재 기사가 여러분의 문제를 해결하지 못하는 경우 AI 엔진은 머신러닝 분석(스마트 모델이 방금 만들어져 부정확한 경우가 있을 수 있음)을 통해 가장 유사한 기사를 추천합니다:
다양한 언어의 JSONJSON은 Javascript 표기법을 사용하여 데이터 구조를 레이아웃하는 데이터 형식입니다. 그러나 Javascript가 코드에서 이러한 구조를 나타낼 수 있는 유일한 언어는 아닙니다. 저는 일반적으로 '객체'{}...
텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
CC BY-SA 2.5, CC BY-SA 3.0 및 CC BY-SA 4.0에 따라 라이센스가 부여됩니다.