2015-02-10 8 views
23

मैं async कार्यों के साथ काम कर रहा हूँ और इससही रास्ते का इंतजार

public class UserService : IUserService 
{ 
    public ILocPrincipal Current 
    { 
     get { return HttpContext.Current.User as ILocPrincipal; } 
    } 
} 

public class ChannelService : IDisposable 
{ 
    // In the service layer 
    public ChannelService() 
      : this(new Entities.LocDbContext(), new UserService()) 
     { 
     } 

    public ChannelService(Entities.LocDbContext locDbContext, IUserService userService) 
    { 
     this.LocDbContext = locDbContext; 
     this.UserService = userService; 
    } 

    public async Task<ViewModels.DisplayChannel> FindOrDefaultAsync(long id) 
    { 
    var currentMemberId = this.UserService.Current.Id; 
    // do some async EF request … 
    } 
} 

// In the controller 
[Authorize] 
[RoutePrefix("channel")] 
public class ChannelController : BaseController 
{ 
    public ChannelController() 
     : this(new ChannelService() 
    { 
    } 

    public ChannelController(ChannelService channelService) 
    { 
     this.ChannelService = channelService; 
    } 

    // … 

    [HttpGet, Route("~/api/channels/{id}/messages")] 
    public async Task<ActionResult> GetMessages(long id) 
    { 
     var channel = await this.ChannelService 
      .FindOrDefaultAsync(id); 

     return PartialView("_Messages", channel); 
    } 

    // … 
} 

मैं है कोड हाल ही में पुनर्संशोधित तरह HttpContext.Current.User उपयोग करते हैं, जैसा कि पहले मैं देने के लिए किया था सेवा के लिए प्रत्येक कॉल पर उपयोगकर्ता। अब मैं इस आलेख को http://trycatchfail.com/blog/post/Using-HttpContext-Safely-After-Async-in-ASPNET-MVC-Applications.aspx पढ़ता हूं और मुझे यकीन नहीं है कि मेरा कोड अभी भी काम करता है या नहीं। क्या कोई इसे संभालने के लिए बेहतर दृष्टिकोण है? मैं उपयोगकर्ता को सेवा के हर अनुरोध पर नहीं देना चाहता हूं।

उत्तर

37

जब तक आपका web.config settings are correct, async/awaitHttpContext.Current के साथ पूरी तरह से अच्छी तरह से काम करता है। मैं सभी "quirks मोड" व्यवहार को हटाने के लिए httpRuntimetargetFramework4.5 पर सेट करने की अनुशंसा करता हूं।

एक बार यह हो जाता है, सादा async/await पूरी तरह से अच्छी तरह से काम करेगा। यदि आप किसी अन्य धागे पर काम कर रहे हैं या यदि आपका await कोड गलत है तो आप केवल समस्याओं में भाग लेंगे।


सबसे पहले, "अन्य धागा" समस्या; ब्लॉग पोस्ट में यह दूसरी समस्या है जिसे आपने लिंक किया है। इस तरह का कोड निश्चित रूप से सही ढंग से काम नहीं करेगा:

async Task FakeAsyncMethod() 
{ 
    await Task.Run(() => 
    { 
    var user = _userService.Current; 
    ... 
    }); 
} 

इस समस्या में असीमित कोड के साथ वास्तव में कुछ भी नहीं है; इसे एक (गैर-अनुरोध) थ्रेड पूल थ्रेड से एक संदर्भ चर पुनर्प्राप्त करने के साथ करना है। यदि आप इसे सिंक्रनाइज़ करने की कोशिश करते हैं तो वही समस्या ठीक होगी।

मूल समस्या यह है कि एसिंक्रोनस संस्करण नकली एसिंक्रोनि का उपयोग कर रहा है। यह अनुचित, खासकर एएसपी.नेट पर।समाधान (अगर यह वास्तव में क्या करने के लिए वास्तविक अतुल्यकालिक काम है, या वास्तव में अतुल्यकालिक) बस नकली एसिंक्रोनस कोड को हटा कर उसे तुल्यकालिक बनाने के लिए है:

void Method() 
{ 
    var user = _userService.Current; 
    ... 
} 

तकनीक से जुड़े हुए ब्लॉग (HttpContext और लपेटकर में सिफारिश की इसे कार्यकर्ता धागे को प्रदान करना) बेहद खतरनाक है। HttpContext को एक समय में केवल एक थ्रेड से एक्सेस करने के लिए डिज़ाइन किया गया है और AFAIK थ्रेडसेफ बिल्कुल नहीं है। तो इसे विभिन्न धागे के बीच साझा करना चोट की दुनिया के लिए पूछ रहा है।


await कोड गलत है, तो यह एक ऐसी ही समस्या का कारण बनता है। ConfigureAwait(false) एक ऐसी तकनीक है जो आमतौर पर लाइब्रेरी कोड में उपयोग की जाती है ताकि रनटाइम को सूचित किया जा सके कि उसे किसी विशिष्ट संदर्भ पर वापस जाने की आवश्यकता नहीं है। इस कोड पर विचार करें:

async Task MyMethodAsync() 
{ 
    await Task.Delay(1000).ConfigureAwait(false); 
    var context = HttpContext.Current; 
    // Note: "context" is not correct here. 
    // It could be null; it could be the correct context; 
    // it could be a context for a different request. 
} 

इस मामले में, समस्या स्पष्ट है। ConfigureAwait(false) एएसपी.नेट को बता रहा है कि शेष मौजूदा विधि को संदर्भ की आवश्यकता नहीं है, और फिर यह तुरंत उस संदर्भ तक पहुंचता है। जब आप अपने इंटरफेस कार्यान्वयन में प्रसंग मूल्यों का उपयोग शुरू, हालांकि, समस्या नहीं के रूप में स्पष्ट है:

async Task MyMethodAsync() 
{ 
    await Task.Delay(1000).ConfigureAwait(false); 
    var user = _userService.Current; 
} 

इस कोड को बस के रूप में गलत है लेकिन स्पष्ट रूप से गलत रूप में नहीं है, क्योंकि संदर्भ एक अंतरफलक के पीछे छिपा है।

तो, सामान्य दिशानिर्देश है: उपयोग ConfigureAwait(false) यदि आप जानते हैं कि विधि उसके संदर्भ (प्रत्यक्ष या परोक्ष) पर निर्भर नहीं करता; अन्यथा, ConfigureAwait का उपयोग न करें। यदि यह करने के लिए अपने डिजाइन में स्वीकार्य है इंटरफ़ेस कार्यान्वयन उनके क्रियान्वयन में संदर्भ है, तो कोई भी तरीका है कि एक इंटरफेस प्रणाली को बुलाती है का उपयोग करना चाहिए नहीं उपयोग ConfigureAwait(false):

async Task MyMethodAsync() 
{ 
    await Task.Delay(1000); 
    var user = _userService.Current; // works fine 
} 

जब तक आप उस दिशानिर्देश का पालन के रूप में, async/awaitHttpContext.Current के साथ पूरी तरह से काम करेगा।

+1

यदि आप जानना चाहते हैं कि आपका एएसपी.NET एप्लिकेशन किस रनटाइम के नीचे चल रहा है तो ब्रेक पॉइंट दबाएं और स्थिर संपत्ति का उपयोग करें: 'HttpRuntime.TargetFramework'। –

2

Async ठीक है। समस्या तब होती है जब आप काम को एक अलग थ्रेड पर पोस्ट करते हैं। आपके आवेदन 4.5+ के रूप में सेटअप है तो अतुल्यकालिक कॉलबैक मूल संदर्भ में तैनात किया जाएगा, ताकि आप भी होगा उचित HttpContext आदि

आप वैसे भी एक अलग सूत्र में साझा राज्य का उपयोग नहीं करना चाहते हैं, और Task रों के साथ, आप शायद ही कभी कि स्पष्ट रूप से संभाल करने की जरूरत है - बस सुनिश्चित करें कि आप तर्क के रूप में अपने सभी आदानों डाल कर, और केवल एक प्रतिक्रिया वापस, बल्कि पढ़ने या किसी साझा राज्य के लिए लिखने की तुलना में (HttpContext जैसे, स्थिर क्षेत्रों आदि)

2

कोई समस्या नहीं है, यदि आपका ViewModels.DisplayChannel अतिरिक्त तर्क के बिना एक साधारण वस्तु है।

एक समस्या हो सकती है, यदि आपके Task "कुछ संदर्भ ऑब्जेक्ट्स" संदर्भों का संदर्भ है, तो f.e. HttpContext.Current पर। ऐसी वस्तुएं अक्सर धागे से जुड़ी होती हैं, लेकिन await के बाद पूरा कोड किसी अन्य धागे में निष्पादित किया जा सकता है।

ध्यान रखें, UseTaskFriendlySynchronizationContext आपकी सभी समस्याओं का समाधान नहीं करता है। अगर हम एएसपी.नेट एमवीसी के बारे में बात कर रहे हैं, तो यह सेटिंग सुनिश्चित करती है कि Controller.HttpContext में await के पहले के रूप में सही मान शामिल है। लेकिन यह सुनिश्चित नहीं करता है कि HttpContext.Current में सही मान है, और await के बाद भी यह शून्य हो सकता है।

+0

यदि मैं आपका उत्तर सही ढंग से समझता हूं। फिर क्या कोई समस्या है यदि मेरे पास async EF अनुरोध अन्य सेवा कॉल (उदा। किसी अन्य सेवा) के बाद है जो अपने उपयोगकर्ता सेवा – Magu

+0

का उपयोग करता है हां, यह है। सादे ऑब्जेक्ट्स को वापस आज़माएं (और ईएफ इकाइयों के लिए आलसी लोडिंग से बचें)। –

संबंधित मुद्दे