asp.net - प्रति वेब अनुरोध एक डीबीकॉन्टेक्स्ट... क्यों?




entity-framework dependency-injection (6)

मैं एंटीटी फ्रेमवर्क के DbContext को कैसे सेट अप करना है, इस बारे में बताते हुए बहुत सारे लेख पढ़ DbContext ताकि विभिन्न डी फ्रेमवर्क का उपयोग करके केवल एक HTTP वेब अनुरोध बनाया और उपयोग किया जा सके।

यह पहली जगह क्यों एक अच्छा विचार है? इस दृष्टिकोण का उपयोग करके आपको क्या फायदे हैं? क्या कुछ स्थितियां हैं जहां यह एक अच्छा विचार होगा? क्या ऐसी चीजें हैं जो आप इस तकनीक का उपयोग कर कर सकते हैं जो आप DbContext प्रति DbContext विधि कॉल को DbContext चालू करते समय नहीं कर सकते हैं?


नोट: यह उत्तर एंटिटी फ्रेमवर्क के DbContext बारे में बात करता है, लेकिन यह किसी भी तरह के कार्य कार्यान्वयन के लिए लागू होता है, जैसे कि LINQ से SQL के DbContext , और DbContext के DbContext

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

मुझे यह कहकर शुरू करना है कि एक DbContext को क्षणिक के रूप में पंजीकृत करना काम कर सकता है, लेकिन आम तौर पर आप एक निश्चित दायरे में काम की ऐसी इकाई का एक उदाहरण चाहते हैं। एक वेब अनुप्रयोग में, वेब अनुरोध की सीमाओं पर इस तरह के दायरे को परिभाषित करने के लिए व्यावहारिक हो सकता है; इस प्रकार एक प्रति वेब अनुरोध जीवनशैली। यह आपको ऑब्जेक्ट्स का एक पूरा सेट उसी संदर्भ में संचालित करने की अनुमति देता है। दूसरे शब्दों में, वे एक ही व्यापार लेनदेन के भीतर काम करते हैं।

यदि आपके पास संचालन का एक सेट होने का कोई लक्ष्य नहीं है, तो उसी संदर्भ में, क्षणिक जीवनशैली ठीक है, लेकिन कुछ चीजें देखने के लिए हैं:

  • चूंकि प्रत्येक ऑब्जेक्ट का अपना उदाहरण मिलता है, प्रत्येक वर्ग जो सिस्टम की स्थिति को बदलता है, को _context.SaveChanges() को कॉल करने की आवश्यकता होती है। _context.SaveChanges() (अन्यथा परिवर्तन खो जाएंगे)। यह आपके कोड को जटिल कर सकता है, और कोड (संदर्भ को नियंत्रित करने की ज़िम्मेदारी) की दूसरी ज़िम्मेदारी जोड़ता है, और एकल जिम्मेदारी सिद्धांत का उल्लंघन है।
  • आपको यह सुनिश्चित करने की ज़रूरत है कि [ DbContext द्वारा लोड और सहेजी गई DbContext ] इस तरह के वर्ग का दायरा कभी नहीं छोड़ें, क्योंकि इन्हें किसी अन्य वर्ग के संदर्भ उदाहरण में उपयोग नहीं किया जा सकता है। यह आपके कोड को बहुत जटिल बना सकता है, क्योंकि जब आपको उन संस्थाओं की आवश्यकता होती है, तो आपको उन्हें आईडी द्वारा फिर से लोड करने की आवश्यकता होती है, जो प्रदर्शन समस्याओं का कारण बन सकती है।
  • चूंकि DbContext लागू करता है, इसलिए आप अभी भी सभी बनाए गए उदाहरणों को DbContext चाहते हैं। यदि आप ऐसा करना चाहते हैं, तो मूल रूप से आपके पास दो विकल्प होते हैं। context.SaveChanges() को कॉल करने के बाद आपको उन्हें उसी विधि में निपटाने की आवश्यकता है। सेव context.SaveChanges() , लेकिन उस स्थिति में व्यवसाय तर्क किसी ऑब्जेक्ट का स्वामित्व लेता है जो इसे बाहर से पारित किया जाता है। दूसरा विकल्प एचटीपी अनुरोध की सीमा पर सभी बनाए गए उदाहरणों को निपटाना है, लेकिन उस स्थिति में आपको अभी भी कंटेनर को यह जानने के लिए कुछ प्रकार की स्कॉइंग की आवश्यकता है जब उन उदाहरणों को डिस्पोज़ किया जाना चाहिए।

एक अन्य विकल्प एक DbContext इंजेक्ट नहीं करना है । इसके बजाय, आप एक DbContextFactory इंजेक्ट DbContextFactory जो एक नया उदाहरण बनाने में सक्षम होता है (मैं अतीत में इस दृष्टिकोण का उपयोग करता था)। इस तरह व्यापार तर्क स्पष्ट रूप से संदर्भ को नियंत्रित करता है। अगर ऐसा दिखाई दे सकता है:

public void SomeOperation()
{
    using (var context = this.contextFactory.CreateNew())
    {
        var entities = this.otherDependency.Operate(
            context, "some value");

        context.Entities.InsertOnSubmit(entities);

        context.SaveChanges();
    }
}

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

नकारात्मकता यह है कि आपको विधि से विधि (जिसे विधि इंजेक्शन कहा जाता है) से DbContext को पास करना होगा। ध्यान दें कि एक अर्थ में यह समाधान 'स्कॉप्ड' दृष्टिकोण जैसा ही है, लेकिन अब दायरे को एप्लिकेशन कोड में ही नियंत्रित किया जाता है (और संभवतः कई बार दोहराया जाता है)। यह वह एप्लिकेशन है जो काम की इकाई बनाने और निपटाने के लिए ज़िम्मेदार है। चूंकि निर्भरता ग्राफ का निर्माण करने के बाद DbContext बनाया गया है, इसलिए कन्स्ट्रक्टर इंजेक्शन तस्वीर से बाहर है और जब आपको एक वर्ग से दूसरी कक्षा में संदर्भ पारित करने की आवश्यकता होती है तो आपको विधि इंजेक्शन में स्थगित करने की आवश्यकता होती है।

विधि इंजेक्शन इतना बुरा नहीं है, लेकिन जब व्यापार तर्क अधिक जटिल हो जाता है, और अधिक कक्षाएं शामिल होती हैं, तो आपको इसे विधि से विधि और कक्षा से कक्षा तक पास करना होगा, जो कोड को बहुत जटिल बना सकता है (मैंने देखा है अतीत में यह)। एक साधारण आवेदन के लिए, यह दृष्टिकोण यद्यपि ठीक होगा।

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

कंटेनर और / या आधारभूत संरचना को इसे संभालने से, आपका एप्लिकेशन कोड बनाने के लिए प्रदूषित नहीं होता है, (वैकल्पिक रूप से) एक यूओडब्ल्यू उदाहरण को प्रतिबद्ध और निपटाना, जो व्यवसाय तर्क को सरल और साफ रखता है (केवल एक जिम्मेदारी)। इस दृष्टिकोण के साथ कुछ कठिनाइयां हैं। उदाहरण के लिए, क्या आप उदाहरण की प्रतिबद्धता और निपटान करते थे?

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

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

class TransactionalCommandHandlerDecorator<TCommand>
    : ICommandHandler<TCommand>
{
    readonly DbContext context;
    readonly ICommandHandler<TCommand> decorated;

    public TransactionCommandHandlerDecorator(
        DbContext context,
        ICommandHandler<TCommand> decorated)
    {
        this.context = context;
        this.decorated = decorated;
    }

    public void Handle(TCommand command)
    {
        this.decorated.Handle(command);

        context.SaveChanges();
    } 
}

यह सुनिश्चित करता है कि आपको केवल एक बार इस बुनियादी ढांचे कोड को लिखना होगा। कोई भी ठोस डी कंटेनर आपको इस तरह के एक सजावटी को सभी ICommandHandler<T> कार्यान्वयन को लगातार तरीके से ICommandHandler<T> अनुमति देता है।


एंटिटी फ्रेमवर्क के साथ देखने के लिए एक और मुद्दा विशेष रूप से नई इकाइयों, आलसी लोडिंग, और फिर उन नई इकाइयों (उसी संदर्भ से) का उपयोग करने के संयोजन का उपयोग करते समय होता है। यदि आप IDbSet.Create (केवल नए बनाम) का उपयोग नहीं करते हैं, तो उस इकाई पर आलसी लोडिंग तब काम नहीं करती जब इसे संदर्भ में से पुनर्प्राप्त किया गया था। उदाहरण:

 public class Foo {
     public string Id {get; set; }
     public string BarId {get; set; }
     // lazy loaded relationship to bar
     public virtual Bar Bar { get; set;}
 }
 var foo = new Foo {
     Id = "foo id"
     BarId = "some existing bar id"
 };
 dbContext.Set<Foo>().Add(foo);
 dbContext.SaveChanges();

 // some other code, using the same context
 var foo = dbContext.Set<Foo>().Find("foo id");
 var barProp = foo.Bar.SomeBarProp; // fails with null reference even though we have BarId set.

माइक्रोसॉफ्ट द्वारा दो विरोधाभासी सिफारिशें हैं और कई लोग डीबीकॉन्टेक्स का पूरी तरह से अलग तरीके से उपयोग करते हैं।

  1. एक सिफारिश है कि "डीबीकॉन्टेक्स को जितनी जल्दी हो सके डिस्पोजेक्ट करें" क्योंकि डीबीकॉन्टेक्स्ट एलीव में डीबी कनेक्शन इत्यादि जैसे मूल्यवान संसाधन हैं ....
  2. अन्य राज्यों में प्रति अनुरोध एक डीबीकॉन्टेक्स्ट अत्यधिक अनुशंसित है

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

नियम 1 का पालन करने वाले बहुत से लोग अपने डीबीकॉन्टेक्स को अपने "रिपोजिटरी पैटर्न" के अंदर रखते हैं और प्रति डेटाबेस क्वेरी का एक नया उदाहरण बनाते हैं ताकि X * DbContext प्रति अनुरोध

वे सिर्फ अपना डेटा प्राप्त करते हैं और संदर्भ ASAP का निपटान करते हैं। यह कई लोगों द्वारा एक स्वीकार्य अभ्यास माना जाता है। हालांकि इसमें आपके डीबी संसाधनों को कम से कम समय पर कब्जा करने का लाभ है, यह स्पष्ट रूप से सभी यूनिटऑफवर्क और कैशिंग कैंडी ईएफ को बलिदान देता है।

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

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

लेकिन कई मामलों में यह सच नहीं है। मैं एक अलग यूनिटऑफवर्क लॉगिंग पर विचार करता हूं इस प्रकार एसिंक थ्रेड में पोस्ट-अनुरोध लॉगिंग के लिए एक नया डीबीकॉन्टेक्स्ट होना पूरी तरह से स्वीकार्य है

तो अंततः यह नीचे चला जाता है कि एक डीबीकॉन्टेक्स्ट का जीवनकाल इन दो मानकों तक ही सीमित है। यूनिटऑफवर्क और थ्रेड


मुझे इसके बारे में क्या पसंद है कि यह इकाई के काम को संरेखित करता है (जैसा कि उपयोगकर्ता इसे देखता है - यानी एक पृष्ठ सबमिट करता है) ओआरएम भावना में इकाई के काम के साथ।

इसलिए, आप पूरे पृष्ठ सबमिशन को लेनदेन कर सकते हैं, जो आप कर सकते हैं अगर आप सीआरयूडी विधियों को एक नया संदर्भ बनाने के साथ उजागर कर रहे थे।


मैं पिछली राय से सहमत हूं। यह कहना अच्छा है कि यदि आप एकल थ्रेड ऐप में डीबीकॉन्टेक्स्ट साझा करने जा रहे हैं, तो आपको अधिक मेमोरी की आवश्यकता होगी। उदाहरण के लिए Azure (एक अतिरिक्त छोटे उदाहरण) पर मेरे वेब एप्लिकेशन की एक और 150 एमबी मेमोरी की आवश्यकता है और मेरे पास प्रति घंटे लगभग 30 उपयोगकर्ता हैं।

यहां वास्तविक उदाहरण छवि है: आवेदन 12 पीएम में तैनात किया गया है


यहां एक भी जवाब वास्तव में सवाल का जवाब नहीं देता है। ओपी ने एक सिंगलटन / प्रति-अनुप्रयोग डीबीकॉन्टेक्स्ट डिज़ाइन के बारे में नहीं पूछा, उन्होंने एक प्रति-वेब अनुरोध अनुरोध के बारे में पूछा और संभावित लाभ क्या हो सकते हैं।

मैं mehdi.me/ambient-dbcontext-in-ef6 संदर्भ दूंगा क्योंकि मेहदी एक शानदार संसाधन है:

संभावित प्रदर्शन लाभ।

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

यह आलसी लोडिंग सक्षम बनाता है।

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

ध्यान रखें कि विपक्ष भी हैं। उस लिंक में विषय पर पढ़ने के लिए कई अन्य संसाधन शामिल हैं।

अगर किसी और ने इस सवाल पर ठोकर खाई है तो इसे पोस्ट करना और उन उत्तरों में अवशोषित नहीं होता है जो वास्तव में प्रश्न को संबोधित नहीं करते हैं।





dbcontext