2015-04-15 8 views
5

हमारे वेब अनुप्रयोगों में हमारे डेटाबेस में विभिन्न प्रकार की तालिकाओं से डेटा की आवश्यकता के लिए यह बहुत आम है। आज आपको एक अनुरोध के लिए 5 या 6 डेटाबेस प्रश्नों को क्रमशः निष्पादित किया जा सकता है। इनमें से कोई भी प्रश्न दूसरे से डेटा पर निर्भर नहीं है, इसलिए वे समानांतर में निष्पादित होने के लिए पूर्ण उम्मीदवार हैं। समस्या DbConcurrencyException अच्छी तरह से ज्ञात है जिसे उसी संदर्भ के विरुद्ध कई प्रश्न निष्पादित किए जाने पर फेंक दिया जाता है।समांतरता और इकाई फ्रेमवर्क

हम आम तौर पर प्रति अनुरोध एक संदर्भ का उपयोग करते हैं और उसके बाद एक भंडार वर्ग है ताकि हम विभिन्न परियोजनाओं में प्रश्नों का पुन: उपयोग कर सकें। जब हम नियंत्रक का निपटारा करते हैं तो हम अनुरोध के अंत में संदर्भ का निपटान करते हैं।

नीचे एक उदाहरण है जो समांतरता का उपयोग करता है, लेकिन अभी भी एक समस्या है!

var fileTask = new Repository().GetFile(id); 
var filesTask = new Repository().GetAllFiles(); 
var productsTask = AllProducts(); 
var versionsTask = new Repository().GetVersions(); 
var termsTask = new Repository().GetTerms(); 

await Task.WhenAll(fileTask, filesTask, productsTask, versionsTask, termsTask); 

प्रत्येक भंडार आंतरिक रूप से अपने स्वयं के संदर्भ पैदा कर रही है, लेकिन जैसा कि अभी है, वे निपटारा नहीं किया जा रहा है। ये एक समस्या है। मुझे पता है कि मैं प्रत्येक रिपोजिटरी पर Dispose पर कॉल कर सकता हूं, लेकिन यह कोड को जल्दी से अव्यवस्थित करना शुरू कर देता है। मैं प्रत्येक क्वेरी के लिए एक रैपर फ़ंक्शन बना सकता हूं जो अपने संदर्भ का उपयोग करता है, लेकिन यह गन्दा लगता है और समस्या के लिए एक दीर्घकालिक समाधान नहीं है।

इस समस्या का समाधान करने का सबसे अच्छा तरीका क्या होगा? मैं क्लाइंट/उपभोक्ता को समांतर में निष्पादित कई प्रश्नों के मामले में प्रत्येक संग्रह/संदर्भ को निपटाने के बारे में चिंता करने की आवश्यकता नहीं है।

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

मैं समानांतरवाद और इकाई की रूपरेखा के आसपास इस तरह के छोटे से चर्चा देख कर हैरान हूँ, इसलिए उम्मीद है कि समुदाय से कुछ अधिक विचारों में आ जाएगा।

संपादित

यहाँ का एक सरल उदाहरण है क्या हमारा भंडार इस तरह दिखता है:

public class Repository : IDisposable { 
    public Repository() { 
     this.context = new Context(); 
     this.context.Configuration.LazyLoadingEnabled = false; 
    } 

    public async Task<File> GetFile(int id) { 
     return await this.context.Files.FirstOrDefaultAsync(f => f.Id == id); 
    } 

    private bool disposed = false; 

    protected virtual void Dispose(bool disposing) { 
     if (!this.disposed) { 
      if (disposing) { 
       context.Dispose(); 
      } 
     } 
     this.disposed = true; 
    } 

    public void Dispose() { 
     Dispose(true); 
     GC.SuppressFinalize(this); 
    } 
} 

जैसा कि आप देख सकते हैं, प्रत्येक भंडार का अपना संदर्भ प्राप्त होता है। इसका मतलब है कि प्रत्येक भंडार का निपटान करने की आवश्यकता है। उदाहरण में मैंने ऊपर दिया, इसका मतलब है कि मुझे Dispose() पर 4 कॉल की आवश्यकता होगी।

समस्या का कारखाना दृष्टिकोण के लिए मेरे विचार की तरह कुछ था निम्नलिखित:

public class RepositoryFactory : IDisposable { 
    private List<IRepository> repositories; 

    public RepositoryFactory() { 
     this.repositories = new List<IRepository>(); 
    } 

    public IRepository CreateRepository() { 
     var repo = new Repository(); 
     this.repositories.Add(repo); 
     return repo;    
    } 

    #region Dispose 
    private bool disposed = false; 

    protected virtual void Dispose(bool disposing) { 
     if (!this.disposed) { 
      if (disposing) { 
       foreach (var repo in repositories) { 
        repo.Dispose(); 
       } 
      } 
     } 
     this.disposed = true; 
    } 

    public void Dispose() { 
     Dispose(true); 
     GC.SuppressFinalize(this); 
    } 
    #endregion 
} 

इस कारखाने मेरी भंडार के उदाहरण बनाने के लिए जिम्मेदार हो सकता है, लेकिन यह भी सभी उदाहरणों यह है का ट्रैक रखने होगा बनाया था। एक बार यह एकल कारखाना वर्ग का निपटारा हो जाने पर यह आंतरिक रूप से बनाए गए प्रत्येक भंडार का निपटान करने के लिए जिम्मेदार होगा।

+0

मेरा मानना ​​है कि यदि आप मैन्युअल रूप से कनेक्शन का प्रबंधन नहीं करते हैं तो एक ईएफ संदर्भ को निपटाने की आवश्यकता नहीं है। इसे प्रत्येक अनुरोध के लिए खोलना और बंद करना चाहिए। संदर्भों का निपटारा नहीं करना मुझे एक गंदे दृष्टिकोण के रूप में हमला करता है, हालांकि। – usr

+0

@usr हमारे पास उत्पादन में किसी और द्वारा लिखा गया कोड है जो सभी संदर्भों का निपटारा नहीं कर रहा है। :) यह काम करता है, लेकिन मैं निश्चित नहीं हूं कि परिणाम क्या हैं या होंगे। चूंकि संदर्भ 'आईडीस्पोजेबल' लागू करता है, इसलिए मैं एक दृष्टिकोण विकसित करना चाहता हूं जो कि क्या हो सकता है के रहस्य को हटा देता है। –

+1

_ "इस समस्या को हल करने का सबसे अच्छा तरीका क्या होगा?" _ - मुझे लगता है कि आपको पहले से पहचाने गए संभावित दृष्टिकोणों पर आपके आपत्तियों के बारे में अधिक विशिष्ट होना चाहिए। "गन्दा" और "अव्यवस्था" से कुछ बेहतर है। तथ्य यह है कि, "गन्दा" और "अव्यवस्था" छिपाने के लिए encapsulation एक आम और वैध तकनीक है, और कुछ प्रकार का एक रैपर encapsulation का एक रूप है। अधिक जानकारी के बिना, आप जो भी प्राप्त करने जा रहे हैं वे अस्पष्ट, राय वाले उत्तर हैं। –

उत्तर

1

आप क्लाइंट को Repository के निपटारे व्यवहार को कॉन्फ़िगर करने के लिए कुछ प्रकार के वैकल्पिक (डिफ़ॉल्ट रूप से गलत) autodispose बिट पास करके कॉन्फ़िगर करने की अनुमति दे सकते हैं। एक कार्यान्वयन कुछ इस तरह दिखेगा:

public class Repository : IDisposable 
{ 
    private readonly bool _autodispose = false; 
    private readonly Lazy<Context> _context = new Lazy<Context>(CreateContext); 

    public Repository(bool autodispose = false) { 
     _autodispose = autodispose; 
    } 

    public Task<File> GetFile(int id) { 
     // public query methods are still one-liners 
     return WithContext(c => c.Files.FirstOrDefaultAsync(f => f.Id == id)); 
    } 

    private async Task<T> WithContext<T>(Func<Context, Task<T>> func) { 
     if (_autodispose) { 
      using (var c = CreateContext()) { 
       return await func(c); 
      } 
     } 
     else { 
      return await func(_context.Value); 
     } 
    } 

    private static Context CreateContext() { 
     var c = new Context(); 
     c.Configuration.LazyLoadingEnabled = false; 
     return c; 
    } 

    public void Dispose() { 
     if (_context.IsValueCreated) 
      _context.Value.Dispose(); 
    } 
} 

नोट: मैं निपटान तर्क उदाहरण के लिए सरल रखा, आपको अपने disposed बिट्स को वापस काम करने की आवश्यकता हो सकती है।

आपकी क्वेरी तरीकों अभी भी सरल एक लाइनर्स हैं, और आवश्यकतानुसार ग्राहक बहुत आसानी से निपटान व्यवहार कॉन्फ़िगर कर सकते हैं, और यहां तक ​​कि ऑटो निपटान स्थितियों में एक भंडार उदाहरण का फिर से उपयोग:

var repo = new Repository(autodispose: true); 
var fileTask = repo.GetFile(id); 
var filesTask = repo.GetAllFiles(); 
var productsTask = AllProducts(); 
var versionsTask = repo.GetVersions(); 
var termsTask = repo.GetTerms(); 

await Task.WhenAll(fileTask, filesTask, productsTask, versionsTask, termsTask); 
+0

यह एक अच्छा विचार है और शुरुआत में मैंने सोचा कि मैं इस समस्या को हल कर सकता हूं, लेकिन ऐसे समय होते हैं जहां मैं संदर्भ का निपटारा नहीं कर सकता क्योंकि रिकॉर्ड को अद्यतन करने की आवश्यकता है। ऐसी कई स्थितियां हैं जहां हम डेटाबेस से मौजूदा रिकॉर्ड पूछते हैं, उपयोगकर्ता से नए डेटा के साथ ऑब्जेक्ट को अद्यतन करते हैं और डेटाबेस में उन परिवर्तनों को प्रतिबद्ध करते हैं। यदि संदर्भ का निपटारा किया गया है तो अद्यतन विफल हो जाएगा। –

+0

ठीक है, ऐसा लगता है कि आपको कॉन्फ़िगर करने योग्य व्यवहार की आवश्यकता है, और इसके आधार पर मैंने अपना जवाब दोबारा लिखा है। मुझे अभी भी नहीं लगता कि फैक्ट्री क्लास की आवश्यकता है। –

+0

यह अच्छा लग रहा है! मुझे यकीन नहीं है कि मैंने उस बिट को कन्स्ट्रक्टर को क्यों नहीं ले जाना था। मैंने आपको +1 दिया और मैं आशा करता हूं कि यह दोपहर या अगले हफ्ते बाद में आज़माएं। –

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