2012-10-31 13 views
6

में एसिंक्रोनस एक्शन के सिंक्रोनस निष्पादन को बल दें क्योंकि एमवीसी 4 असीमित बाल क्रियाओं का समर्थन नहीं करता है (Html.Action के माध्यम से) मैं सिंक्रोनस निष्पादन बाल क्रियाओं को बल देने के लिए एक तरीका ढूंढ रहा हूं।एएसपी.नेट एमवीसी 4

public class FooAsyncController : Controller { 
    public async Task<ActionResult> IndexAsync() { 
     var model = await service.GetFoo().ConfigureAwait(false); 
     return View(model); 
    } 
} 

public class FooSyncController : FooAsyncController { 
    public ActionResult Index() { 
     return IndexAsync().Result; // blocking call 
    } 
} 

हालांकि, बाद से हम अपने नियंत्रक कार्यों के सभी पर बच्चे कार्रवाई अनुरोध अनुमति देते हैं, हर नियंत्रक के लिए यह कर एक असली PITA है: इस सीमा के लिए एक साधारण तरीके को अपने सभी नियंत्रकों कार्यों के तुल्यकालिक संस्करणों प्रदान करना है।

क्या ढांचे में कोई विस्तारशीलता बिंदु है जहां हम किसी कार्रवाई के वापसी मूल्य का निरीक्षण कर सकते हैं और यदि यह Task<T> देता है और हम एक बच्चे की कार्रवाई को संभालने में सक्षम होते हैं, तो एक सिंक्रोनस कॉल को मजबूर करते हैं?

+1

पहली बात यह है कि मन में आता है एक नया विस्तार विधि 'Html.ActionAsync' कि बस पाइपलाइन लागू करेगा की तरह आप चाहते हैं, लेकिन जोड़ने के लिए' सहायक विधि के अंत करने के लिए कॉल .Result' है शरीर, इस प्रकार आपके एसिंक कॉल को सिंक कॉल में बदल देता है। निश्चित रूप से दिलचस्प समस्या है। – Tejs

+1

यह मैं था, मैं एक नया नियंत्रक बनाउंगा जो CreateActionInvoker या हैंडल अज्ञात एक्शन को ओवरराइड करता है और प्रतिबिंब के साथ एक सिंक्रोनस आमंत्रण करने के लिए एक लक्ष्य नियंत्रक को देखता है। आपको केवल नियंत्रक को एक बार लागू करना होगा और इसे किसी भी एसिंक एक्शन के लिए पुन: उपयोग करना होगा। –

+0

@ टीजेएस हाँ यह विचार समाधान होगा हालांकि ढांचे का यह क्षेत्र बहुत असीमित अनुकूल नहीं है। समस्या 'HttpServerUtilityBase.Execute' के अंदर कहीं गहरी लगती है। @ थिकिंगसाइट्स - अधिक जानकारी के साथ उत्तर क्यों पोस्ट नहीं करें? यह एक अच्छा कामकाज की तरह लगता है। –

उत्तर

3

एएसपी.नेट एमवीसी स्रोत कोड के माध्यम से घंटों के लिए सबसे अच्छा समाधान (मैं प्रत्येक नियंत्रक कार्रवाई के सिंक्रोनस संस्करणों को बनाने के अलावा) के साथ आने में सक्षम हूं, मैन्युअल रूप से एसिंक एक्शन के लिए एक्शन डिस्क्रिप्टर का आह्वान करना है Controller.HandleUnknownAction के भीतर विधियां।

मैं इस कोड से विशेष रूप से खुश नहीं हूं और मुझे उम्मीद है कि इसे बेहतर किया जा सकता है, लेकिन यह काम करता है।

विचार जानबूझकर एक अमान्य कार्रवाई ("_" के साथ उपसर्ग) का अनुरोध करना है जो नियंत्रक पर HandleUnknownAction विधि का आह्वान करेगा। यहां हम एक मिलान एसिंक एक्शन की तलाश करते हैं (पहले actionName से अंडरस्कोर को हटाकर) और AsyncActionDescriptor.BeginExecute विधि का आह्वान करें। EndExecute विधि को तुरंत कॉल करके हम प्रभावी ढंग से एक्शन डिस्क्रिप्टर सिंक्रनाइज़ निष्पादित कर रहे हैं।

public ActionResult Index() 
{ 
    return View(); 
} 


public async Task<ActionResult> Widget(int page = 10) 
{ 
    var content = await new HttpClient().GetStringAsync("http://www.foo.com") 
     .ConfigureAwait(false); 
    ViewBag.Page = page; 
    return View(model: content); 
} 

protected override void HandleUnknownAction(string actionName) 
{ 
    if (actionName.StartsWith("_")) 
    { 
     var asyncActionName = actionName.Substring(1, actionName.Length - 1); 
     RouteData.Values["action"] = asyncActionName; 

     var controllerDescriptor = new ReflectedAsyncControllerDescriptor(this.GetType()); 
     var actionDescriptor = controllerDescriptor.FindAction(ControllerContext, asyncActionName) 
      as AsyncActionDescriptor; 

     if (actionDescriptor != null) 
     { 
      AsyncCallback endDelegate = delegate(IAsyncResult asyncResult) 
      { 

      }; 

      IAsyncResult ar = actionDescriptor.BeginExecute(ControllerContext, RouteData.Values, endDelegate, null); 
      var actionResult = actionDescriptor.EndExecute(ar) as ActionResult; 

      if (actionResult != null) 
      { 
       actionResult.ExecuteResult(ControllerContext); 
      } 
     } 
    } 
    else 
    { 
     base.HandleUnknownAction(actionName); 
    } 
} 

दृश्य

<h2>Index</h2> 

@Html.Action("_widget", new { page = 5 }) <!-- note the underscore prefix --> 

मैं लगभग निश्चित है वहाँ Controller.BeginExecute अधिभावी द्वारा एक बेहतर तरीका है हूँ। डिफ़ॉल्ट कार्यान्वयन नीचे देखा जा सकता है। विचार तुरंत Controller.EndExecuteCore निष्पादित करना होगा हालांकि मुझे अब तक कोई सफलता नहीं मिली है।

protected virtual IAsyncResult BeginExecute(RequestContext requestContext, AsyncCallback callback, object state) 
{ 
    if (DisableAsyncSupport) 
    { 
     // For backwards compat, we can disallow async support and just chain to the sync Execute() function. 
     Action action =() => 
     { 
      Execute(requestContext); 
     }; 

     return AsyncResultWrapper.BeginSynchronous(callback, state, action, _executeTag); 
    } 
    else 
    { 
     if (requestContext == null) 
     { 
      throw new ArgumentNullException("requestContext"); 
     } 

     // Support Asynchronous behavior. 
     // Execute/ExecuteCore are no longer called. 

     VerifyExecuteCalledOnce(); 
     Initialize(requestContext); 
     return AsyncResultWrapper.Begin(callback, state, BeginExecuteCore, EndExecuteCore, _executeTag); 
    } 
} 
संबंधित मुद्दे