2012-12-15 32 views
9

के आसपास एक काम की तलाश में मुझे पता है कि प्रतीक्षा खंड में उपयोग नहीं किया जा सकता है। हालांकि मुझे अब तक इस समस्या से संबंधित समस्या का सामना नहीं करना पड़ा है ...पकड़ खंड में वर्जित प्रतीक्षा करें।

असल में मेरे पास एक परत है जो आने वाले अनुरोध प्राप्त करने, उन्हें संसाधित करने, संदेशों से संदेश बनाने और संदेशों को किसी अन्य परत पर पास करने के लिए ज़िम्मेदार है संदेश भेजने के लिए जिम्मेदार है।

अगर संदेश भेजने के दौरान कुछ गलत हो जाता है, तो कस्टम अपवाद फेंक दिया जाता है, और संदेश भेजने वाली परत द्वारा पकड़ा जाता है। इस बिंदु पर, इस संदेश के लिए एक विफलता रिकॉर्ड डीबी में डाला जाना चाहिए (कुछ समय लगता है, इसलिए async), और अपवाद को ऊपरी परत पर प्रचारित किया जाना चाहिए जो अनुरोध करने वाले क्लाइंट को त्रुटि प्रतिक्रिया भेजने के प्रभारी है ।

public async Task ProcessRequestAsync(Request req) 
{ 
    int statusCode = 0; 

    try 
    {  
     await SendMessageAsync(new Message(req));   
    } 
    catch(MyCustomException ex) 
    { 
     statusCode = ex.ErrorCode; 
    } 

    await SendReponseToClientAsync(req, statusCode); 
} 


public async Task SendMessageAsync(Message msg) 
{ 
    try 
    {   
     // Some async operations done here 
    } 
    catch (MyCustomException ex) 
    {  
     await InsertFailureForMessageInDbAsync(msg, ex.ErrorCode); // CAN'T DO THAT 
     throw; 
    } 
} 

बेशक अनुरोध प्रसंस्करण परत डीबी के बारे में कुछ नहीं जानता, यह सिर्फ एक संदेश के निर्माण के प्रभार में है, संदेश के लिए संदेश गुजर:

यहाँ नीचे दिए गए उदाहरण इस प्रयोजन के लिए कुछ बहुत ही सरल कोड है प्रसंस्करण परत, और ग्राहक को प्रतिक्रिया (सकारात्मक या नकारात्मक) भेजें।

तो मुझे लगता है कि यह समझ में आता है ... यदि कोई अपवाद होता है, तो मेरा "व्यवसाय" परत डीबी में विफलता रिकॉर्ड डालना चाहता है और अपवाद को फिर से भरना चाहता है ताकि "अनुरोध" प्रसंस्करण परत आवश्यक हो सके (इसमें संदर्भ, ग्राहक को नकारात्मक प्रतिक्रिया वापस भेजें)।

क्या अपवादों का इस तरह उपयोग नहीं किया जाना चाहिए? यह मेरे लिए साफ लगता है लेकिन तथ्य यह है कि मैं पकड़ने वाले खंड में ऐसा नहीं कर सकता हूं, मुझे लगता है कि डिजाइन के साथ शायद एक कोड गंध हो सकती है (भले ही मैं एक परत में अपवाद को संभालने का विचार करता हूं और फिर इसे फिर से कुछ अलग हैंडलिंग करने के लिए ऊपरी परत के साथ-साथ मुझे लगता है जैसे यह वास्तव में अपवादों के लिए बनाया गया है)।

कोई विचार इसके आसपास है?

धन्यवाद!

+1

ऐसा लगता है कि आप प्रेषण परत में अपवाद पकड़ सकते हैं, और बस कॉलर को स्थिति ऑब्जेक्ट वापस कर सकते हैं। आप 'स्टेटस' प्रकार के इस स्टेटस ऑब्जेक्ट का एक फ़ील्ड भी बना सकते हैं ताकि आवश्यकता होने पर बाद में इसे फिर से फेंक दिया जा सके। – dlev

+1

आप फ़ंक्शन को क्यों कॉल नहीं करते हैं और इसके लिए प्रतीक्षा नहीं करते हैं? – Rafael

+0

@dlev: विचार के लिए धन्यवाद, अभी भी मुझे कुछ जगह पर वापसी की स्थिति मिश्रण करने और अन्य जगह पर अपवाद का उपयोग करने के लिए वास्तव में अच्छा महसूस नहीं होता है। विकल्प को स्टेटस कोड (या ऑब्जेक्ट) या अपवाद फेंकने के बीच किया जाना चाहिए, लेकिन दोनों की मिश्रण नहीं करना (रिपोर्टिंग त्रुटियों के लिए मुझे "अच्छा" डिज़ाइन दिशानिर्देशों से याद है)। – darkey

उत्तर

6

मैंने इसमें कुछ बार भी भाग लिया है।

public async Task SendMessageAsync(Message msg) 
{ 
    try 
    {   
    // Some async operations done here 
    } 
    catch (MyCustomException ex) 
    {  
    var _ = InsertFailureForMessageInDbAsync(msg, ex.ErrorCode); 
    throw; 
    } 
} 

ध्यान दें कि InsertFailureForMessageInDbAsync से किसी भी अपवाद डिफ़ॉल्ट रूप से नजरअंदाज कर दिया जाएगा:

के रूप में राफेल टिप्पणी की, तो आप सिर्फ InsertFailureForMessageInDbAsync का परिणाम उपेक्षा कर सकता है।

आपका दूसरा विकल्प अधिक जटिल है:

public async Task DoSendMessageAsync(Message msg) 
{ 
    // Some async operations done here 
} 

public async Task SendMessageAsync(Message msg) 
{ 
    var task = DoSendMessageAsync(msg); 
    MyCustomException exception = null; 
    try 
    { 
    await task; 
    return; 
    } 
    catch (MyCustomException ex) 
    { 
    exception = ex; 
    } 

    await Task.WhenAll(task, InsertFailureForMessageInDbAsync(msg, exception.ErrorCode)); 
} 

यह अतुल्यकालिक रूप से अपवाद को संभालने और एक Task कोई वास्तविक AggregateExption (दोनों अपवाद युक्त अगर InsertFailureForMessageInDbAsync एक फेंक करता है) है कि वापस आ जाएगी।

दुर्भाग्यवश, await दूसरे अपवाद को अनदेखा कर देगा। क्या तुम सच में सभी अपवादों को ऊपर से पारित कर दिया चाहते हैं, आप कुछ इस तरह के साथ अंतिम पंक्ति (await Task.WhenAll...) की जगह ले सकता:

Exception exception2 = null; 
try 
{ 
    await InsertFailureForMessageInDbAsync(msg, exception.ErrorCode); 
} 
catch (Exception ex) 
{ 
    exception2 = ex; 
} 

if (exception2 == null) 
    throw new AggregateException(exception); 
else 
    throw new AggregateException(exception, exception2); 

लेकिन वह बहुत जटिल बिल्कुल पैटर्न की तरह आप बार-बार करना चाहते है, और नहीं। यदि संभव हो, तो मैं केवल लॉगिंग परिणाम को अनदेखा कर दूंगा क्योंकि राफेल ने सिफारिश की थी।

+0

अच्छा स्टीफन! कहूंगा कि मैं इस पर जाने के लिए एक और अधिक आसान तरीका की उम्मीद कर रहा था, इसलिए मैंने रिफ्लेक्सियन में जितना दूर किया था उतना नहीं गया। लेकिन आखिरकार, इसे संभालने का कोई आसान तरीका नहीं है;) वैसे भी आपके उत्तर के लिए बहुत बहुत धन्यवाद, मुझे लगता है कि मैं बस आसान तरीका जाऊंगा, लेकिन कम से कम मुझे पता चलेगा कि "कठिन" तरीका वास्तव में कैसे जाना है जरूरत है। – darkey