10

मैं एएसपी.नेट कोर और ईएफ कोर का उपयोग कर रहा हूं जिसमें SaveChanges और SaveChangesAsync है।मैं ईएफ के गैर-एसिंक SaveChanges से एसिंक विधि को सुरक्षित रूप से कैसे कॉल करूं?

public async Task LogAndAuditAsync() { 
    // do async stuff 
} 

public override int SaveChanges { 
    /*await*/ LogAndAuditAsync();  // what do I do here??? 
    return base.SaveChanges(); 
} 

public override async Task<int> SaveChangesAsync { 
    await LogAndAuditAsync(); 
    return await base.SaveChanges(); 
} 

समस्या तुल्यकालिक SaveChanges() है:

मेरी DbContext में डेटाबेस के लिए बचत, से पहले, मैं कुछ लेखा परीक्षा/प्रवेश करते हैं।

मैं हमेशा "एसिंक सभी तरह से नीचे" करता हूं, लेकिन यहां यह संभव नहीं है। मैं LogAndAudit() और LogAndAuditAsync() रखने के लिए फिर से डिजाइन कर सकता हूं लेकिन यह DRY नहीं है और मुझे कोड के दर्जनों अन्य प्रमुख टुकड़ों को बदलने की आवश्यकता होगी जो मेरे नहीं हैं।

इस विषय के बारे में कई अन्य प्रश्न हैं, और सभी सामान्य और जटिल और बहस से भरे हुए हैं। मुझे इस विशिष्ट मामले में सबसे सुरक्षित दृष्टिकोण जानने की आवश्यकता है।

तो, SaveChanges() में, मैं सुरक्षित रूप से और सिंक्रनाइज़ रूप से डेडलॉक्स के बिना एसिंक विधि को कैसे कॉल करूं?

+0

क्या आपने प्रतीक्षा की कोशिश की कोशिश की है। Run (() => {...}); –

+0

@ एच। हेर्ज़ल हाँ जो काम करता है। लेकिन ऐसा नहीं है कि मैं इसे संकलित करने या चलाने के लिए नहीं मिल सकता। यह है कि मुझे यह जानने की जरूरत है कि, इस विशेष मामले में, * सबसे सुरक्षित तरीका * क्या है, जो डेडलॉक्स की संभावना से बचाता है। सिंक-ओवर-एसिंक बहुत गलत हो जाना आसान है। – grokky

+0

समस्या को सिस्टेप करना, क्या लॉगिंग/ऑडिटिंग के लिए कतार का उपयोग करना संभव है और फिर लॉग/ऑडिट ऑपरेशन प्रकृति द्वारा एसिंक हो जाता है, इसलिए आपको एसिंक/बिल्कुल प्रतीक्षा करने की आवश्यकता नहीं है? – Menahem

उत्तर

0

सिंक-ओवर-एसिंक करने के कई तरीके हैं, और प्रत्येक के पास यह गेटचा है। लेकिन मुझे यह जानने की जरूरत है कि इस विशिष्ट उपयोग केस के लिए सबसे सुरक्षित कौन सा है।

जवाब उपयोग करने के लिए है स्टीफन Cleary के "Thread Pool Hack":

Task.Run(() => LogAndAuditAsync()).GetAwaiter().GetResult(); 

कारण यह है कि विधि के भीतर, केवल अधिक डेटाबेस काम किया जाता है है, और कुछ नहीं। मूल सिंक्रनाइज़ेशन संदर्भ की आवश्यकता नहीं है - ईएफ कोर के DbContext के भीतर आपको एएसपी.नेट कोर के HttpContext तक पहुंच की आवश्यकता नहीं है!

इसलिए थ्रेड पूल में ऑपरेशन को ऑफ़लोड करना सबसे अच्छा है, और डेडलॉक्स से बचें।

9

एक गैर async विधि से एक async विधि कॉल करने का सबसे सरल तरीका GetAwaiter().GetResult() उपयोग करने के लिए है:

public override int SaveChanges { 
    LogAndAuditAsync().GetAwaiter().GetResult(); 
    return base.SaveChanges(); 
} 

यह सुनिश्चित होगा कि एक अपवाद LogAndAuditAsync में फेंक दिया SaveChanges में एक AggregateException के रूप में प्रकट नहीं होता है। इसके बजाय मूल अपवाद प्रचारित है।

हालांकि, यदि कोड एक विशेष सिंक्रनाइज़ेशन संदर्भ पर निष्पादित कर रहा है जो सिंक-ओवर-एसिंक (उदा। एएसपी.नेट, विनफॉर्म और डब्ल्यूपीएफ) करते समय डेडलॉक हो सकता है तो आपको अधिक सावधान रहना होगा।

हर बार LogAndAuditAsync में कोड await का उपयोग करता है, यह एक कार्य पूरा करने की प्रतीक्षा करेगा। यदि इस कार्य को सिंक्रनाइज़ेशन संदर्भ पर निष्पादित करना है जो वर्तमान में कॉल द्वारा LogAndAuditAsync().GetAwaiter().GetResult() पर अवरुद्ध है, तो आपके पास डेडलॉक है।

इससे बचने के लिए आपको LogAndAuditAsync में सभी await कॉल जोड़ने की आवश्यकता है। जैसे

await file.WriteLineAsync(...).ConfigureAwait(false); 

सूचना है कि इस await के बाद कोड तुल्यकालन संदर्भ से बाहर क्रियान्वित (कार्य पूल अनुसूचक पर) जारी रहेगा।

Task.Run(() => LogAndAuditAsync()).GetAwaiter().GetResult(); 

यह अभी भी तुल्यकालन संदर्भ को अवरुद्ध कर देगा लेकिन LogAndAuditAsync कार्य पूल अनुसूचक पर निष्पादित और नहीं गतिरोध होगा:

अगर यह संभव नहीं है आपका अंतिम विकल्प कार्य पूल अनुसूचक पर कोई नया कार्य शुरू करने के लिए है क्योंकि इसे अवरुद्ध सिंक्रनाइज़ेशन संदर्भ में प्रवेश करने की आवश्यकता नहीं है।

+0

मुझे इस वाक्यविन्यास से अवगत है, लेकिन ईएफ कोर और एएसपी.नेट कोर में दृढ़ता के लिए, इस उपयोग मामले में यह सबसे सुरक्षित तरीका क्यों है? सिंक-ओवर-एसिंक करने के कई तरीके हैं, यह उनमें से एक है। – grokky

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