मुझे एक नया DbContext कब बनाना चाहिए ()


83

मैं वर्तमान में DbContextइस के समान उपयोग कर रहा हूं :

namespace Models
{
    public class ContextDB: DbContext
    {

        public DbSet<User> Users { get; set; }
        public DbSet<UserRole> UserRoles { get; set; }

        public ContextDB()
        {

        }
    }
}

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

ContextDB _db = new ContextDB();

इस बारे में सोच रहे हैं .. जब वहाँ एक आगंतुक कई DbContexts सक्रिय हो सकते हैं .. यानी कर रहे हैं। अगर UserRepository का उपयोग करने वाले नियंत्रक पर जाने में संकोच हो रहा है .. तो यह सबसे अच्छा विचार नहीं हो सकता है, और मुझे इसके बारे में कुछ प्रश्न मिले हैं

  1. मुझे एक नया DbContext कब बनाना चाहिए / क्या मेरे पास एक वैश्विक संदर्भ होना चाहिए जिससे मैं गुजरता हूं?
  2. क्या मेरा एक वैश्विक संदर्भ हो सकता है जिसका मैं सभी स्थानों पर पुन: उपयोग करूं?
  3. क्या इससे प्रदर्शन प्रभावित होता है?
  4. हर कोई ऐसा कैसे कर रहा है?

मैं झंडा पड़ा क्योंकि डुप्लिकेट - देख stackoverflow.com/questions/12871666/linq-and-datacontext एक बहुत अच्छी चर्चा के लिए
SAJ14SAJ

2
इस मामले में मैं निर्भरता इंजेक्शन (उदाहरण के लिए Ninject) का उपयोग करता हूं, इसलिए यह DbContextप्रति अनुरोध एक बना देगा । मैं सर्विस लेयर भी बनाऊंगा। इस SO प्रश्न और उत्तर की
Zbigniew

जवाबों:


82

मैं एक आधार नियंत्रक का उपयोग करता हूं जो एक DataBaseसंपत्ति को उजागर करता है जो व्युत्पन्न नियंत्रक तक पहुंच सकता है।

public abstract class BaseController : Controller
{
    public BaseController()
    {
        Database = new DatabaseContext();
    }

    protected DatabaseContext Database { get; set; }

    protected override void Dispose(bool disposing)
    {
        Database.Dispose();
        base.Dispose(disposing);
    }
}

मेरे आवेदन में सभी नियंत्रकों से व्युत्पन्न BaseControllerऔर इस तरह से उपयोग किया जाता है:

public class UserController : BaseController
{
    [HttpGet]
    public ActionResult Index()
    {
        return View(Database.Users.OrderBy(p => p.Name).ToList());
    }
}

अब आपके सवालों का जवाब देने के लिए:

मुझे एक नया DbContext कब बनाना चाहिए / क्या मेरे पास एक वैश्विक संदर्भ होना चाहिए जिससे मैं गुजरता हूं?

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

कोशिश मत करो और एक वैश्विक संदर्भ है (यह नहीं है कि वेब अनुप्रयोग कैसे काम करते हैं)।

क्या मेरे पास एक वैश्विक संदर्भ हो सकता है जिसका मैं सभी स्थानों पर पुन: उपयोग करूं?

नहीं, यदि आप इसके चारों ओर एक संदर्भ रखते हैं, तो यह सभी अपडेट, परिवर्धन, विलोपन आदि का ट्रैक रखेगा और यह आपके एप्लिकेशन को धीमा कर देगा और यहां तक ​​कि आपके एप्लिकेशन में कुछ सूक्ष्म कीड़े दिखाई दे सकते हैं।

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

व्यक्तिगत रूप से, मैं DbContextसीधे सबसे अधिक रिपॉजिटरी उदाहरणों के रूप में उजागर करना पसंद करता हूं, जिन्हें मैंने DbContextवैसे भी पतले रैपर के रूप में देखा है।

क्या इससे प्रदर्शन प्रभावित होता है?

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

हर कोई ऐसा कैसे कर रहा है?

निर्भर करता है।

कुछ लोग इसे बनाए जाने पर अपने नियंत्रक के संदर्भ के ठोस उदाहरण को पारित करने के लिए एक निर्भरता इंजेक्शन ढांचे का उपयोग करना पसंद करते हैं। दोनों विकल्प ठीक हैं। एक छोटे पैमाने के अनुप्रयोग के लिए मेरा अधिक उपयुक्त है जहाँ आप जानते हैं कि जिस विशिष्ट डेटाबेस का उपयोग किया जा रहा है वह बदलने वाला नहीं है।

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


4
मेरे पास इसे जोड़ने के लिए कुछ भी नहीं है, इसके अलावा अन्य लोगों के पोस्ट में प्रतिबिंबित मेरी अपनी कुछ वरीयताओं को देखना अच्छा है। मैं वास्तव में रिपॉजिटरी उदाहरणों को कभी नहीं समझ पाया हूं, जिसके परिणामस्वरूप एक और परत DbContext पर जोड़ी गई है, वास्तव में कुछ भी जोड़ने के बिना, और मैं एक बेस क्लास बनाने का प्रशंसक हूं जो एक संरक्षित DbContext को उजागर करता है।
Dark_perfect

4
जोड़ना चाहते थे कि यह उत्तर बहुत अच्छा है लेकिन अब ईएफ 6 की रिलीज के साथ आउट ऑफ डेट है जो प्रत्येक उपयोग के बाद स्वचालित रूप से संदर्भ का निपटान करता है। जैसे कि ऐसे परिदृश्य हैं जहां प्रति सत्र (वैश्विक) संदर्भ बनाना ठीक है। यदि आप संग्रहीत कार्यविधियों के लिए संपर्क के लिए EF6 का उपयोग कर रहे हैं और डेटा लॉक करना कोई समस्या नहीं है, तो संभवतः आधार नियंत्रक में एक बार संदर्भ बनाना और सभी नियंत्रकों के लिए आदर्श है कि डेटाबेस का उपयोग आवश्यक है जो आधार से विरासत में मिला है। सबसे सही उत्तर यह है कि आपको तकनीक का संयम रखने और अपनी वास्तुकला के लिए सही पैटर्न लागू करने की आवश्यकता है।
Atters

क्या होगा यदि मैं नियंत्रक कार्रवाई से कुछ मॉडल विधि को कॉल करना चाहता हूं जो DbContext का उपयोग करें? मैं इसे मॉडल विधि में कैसे पास कर सकता हूं?
आरएमजितोव

फिर आप नियंत्रक में डेटाबेस क्वेरी और व्यावसायिक तर्क कर रहे हैं जहां वे नहीं हैं। आपको एक ऐसी सेवा बनानी चाहिए जिसे आपके नियंत्रक से कहा जाता है। यानी आपका UserController आपके UserService में एक विधि कहता है।
फ्रेड

@Fred, हाँ, और मैं कैसे DbContext पास करने के लिए UserService, साधारण पैरामीटर द्वारा कार्य करने के लिए या नहीं?
RMazitov

10

मैं अपने स्वयं के अनुभव का जवाब देने की कोशिश करता हूं।

1. मुझे एक नया DbContext कब बनाना चाहिए / क्या मेरे पास एक वैश्विक संदर्भ होना चाहिए जिससे मैं गुजरता हूं?

निर्भरता-इंजेक्शन द्वारा प्रसंग को इंजेक्ट किया जाना चाहिए और अपने आप से त्वरित नहीं होना चाहिए। बेस्ट-प्रैक्टिस यह निर्भरता-इंजेक्शन द्वारा एक स्कोप सेवा के रूप में बनाई गई है। (प्रश्न 4 पर मेरा उत्तर देखें)

कृपया नियंत्रक> BusinessLogic> Repository जैसी उचित स्तरित एप्लिकेशन संरचना का उपयोग करने पर भी विचार करें। इस मामले में ऐसा नहीं होगा कि आपके नियंत्रक को db-reference प्राप्त होता है, लेकिन इसके बजाय रिपॉजिटरी। नियंत्रक में db-reference को इंजेक्ट / इंस्टेंट करना मुझे बताता है कि आपका एप्लिकेशन आर्किटेक्चर एक ही स्थान पर कई जिम्मेदारियों को मिलाता है, जो - किसी भी परिस्थिति में - मैं अनुशंसा नहीं कर सकता।

2. क्या मेरे पास एक वैश्विक संदर्भ हो सकता है जिसका मैं सभी स्थानों पर पुन: उपयोग करूं?

हां आपके पास हो सकता है लेकिन सवाल " क्या मेरे पास होना चाहिए ..." -> नहीं। प्रसंग का अर्थ है कि आपके भंडार को बदलने के लिए प्रति अनुरोध और उसके बाद फिर से उपयोग किया जाए।

3. क्या इससे प्रदर्शन प्रभावित होता है?

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

जब कई सूत्र एक ही संदर्भ में एक ही बार एक्सेस करते हैं तो आपको अपवाद और कई त्रुटियां भी मिलेंगी।

4. बाकी सब लोग ऐसा कैसे कर रहे हैं?

DBContext एक कारखाने द्वारा निर्भरता-इंजेक्शन के माध्यम से इंजेक्शन; स्कोप:

services.AddDbContext<UserDbContext>(o => o.UseSqlServer(this.settings.DatabaseOptions.UserDBConnectionString));

मुझे उम्मीद है कि मेरे जवाब जहां मदद करेंगे।


क्या होगा यदि मैं स्वयं कॉन्फ़िगर विधि में Startup.cs में DBContext का उपयोग करना चाहता हूं? मेरे पास एक OICD मिडलवेयर है, जहाँ मुझे DB को एक्सेस करने की आवश्यकता है, लेकिन मैं DBContext को एक्सेस नहीं कर सकता या मुझे नहीं पता कि कैसे?
6

कॉन्फ़िगरशिपर्स विधि में, आपका DBContext संभवतः अनुपलब्ध है क्योंकि आपने इसे वहां कॉन्फ़िगर किया था। ServiceProvider जिसमें से आप वास्तव में रनटाइम पर अपना DBContext प्राप्त करते हैं, सबसे पहले कॉन्फिगर () ("कॉन्फिगर सर्विसेज"!) विधि में उपलब्ध होगा। वहां, आप "app.ApplicationServices.GetRequiredService <MyDbContext> ();" टाइप करके ApplicationBuilder का उपयोग करके संदर्भ का अनुरोध कर सकते हैं। (अपने संदर्भ के वर्गनाम के साथ MyDbContext बदलें)।
रविर

यदि नियंत्रकों को एक रिपॉजिटरी इंजेक्शन मिलता है, तो वे (कंट्रोलर) परिवर्तन कैसे बचाते हैं? मान लीजिए कि मैं डेटाबेस में कुछ डालने के लिए POST अनुरोध भेज रहा हूं, नियंत्रक अनुरोध को संभालता है, नए बनाए गए ऑब्जेक्ट को जोड़ने के लिए रिपॉजिटरी का उपयोग करता है .. तो क्या? कौन परिवर्तन जारी रखता है?
MMalke

@Malke रिपोजिटरी ऐसा करता है। इसमें आमतौर पर एक फ़ंक्शन "SaveData (डेटा myData)" होता है और फिर रिपॉजिटरी डेटा-क्लास के लिए अपने एंटिटी-फ्रेमवर्क सिबलिंग का एक उदाहरण बनाता है, इसे dbcontext में dbset के अनुसार जोड़ता है और फिर इसे SaveChanges () कहता है। नियंत्रक केवल एक चीज करता है जिसे रिपॉजिटरी कहा जाता है। सेवदाटा (myData) फ़ंक्शन।
रविवर जूल २ Rav

1

अभी मैं इस दृष्टिकोण की कोशिश कर रहा हूं, जब आप ऐसे कार्यों को कहते हैं जो इसका उपयोग नहीं करते हैं, तो संदर्भ को तत्काल बताने से बचा जाता है।

public abstract class BaseController : Controller
{
    public BaseController() { }

    private DatabaseContext _database;
    protected DatabaseContext Database
    {
        get
        {
            if (_database == null)
                _database = new DatabaseContext();
            return _database;
        }
    }

    protected override void Dispose(bool disposing)
    {
        if (_database != null)
            _database.Dispose();
        base.Dispose(disposing);
    }
}

2
जबकि आपके दृष्टिकोण में कुछ भी गलत नहीं है, मेरा मानना ​​है कि एक एंटिटी फ्रेमवर्क संदर्भ आलसी फैशन में सब कुछ करेगा और जब तक आप वास्तव में डेटाबेस तक नहीं पहुंचते तब तक कोई वास्तविक कार्य नहीं किया जाता है। तो एक ईएफ संदर्भ बनाने का ओवरहेड बहुत छोटा होना चाहिए।
मार्टिन लेवरेज

मैंने कुछ शोध किया और ऐसा लगता है कि यह सही है। मैंने जो कुछ किया उसकी तुलना करके कुछ सरल परीक्षण किया GC.GetTotalMemory()(पूर्ण नहीं, लेकिन यह वही है जो मैंने पाया) और अंतर कभी भी 8 केबी से अधिक नहीं था।
एंड्रयू

0

यह स्पष्ट रूप से एक पुराना सवाल है, लेकिन यदि आपके DI का उपयोग कर आप ऐसा कुछ कर सकते हैं और अनुरोध के जीवनकाल के लिए अपनी सभी वस्तुओं को स्कोप कर सकते हैं

 public class UnitOfWorkAttribute : ActionFilterAttribute
    {
        public override void OnActionExecuting(HttpActionContext actionContext)
        {
            var context = IoC.CurrentNestedContainer.GetInstance<DatabaseContext>();
            context.BeginTransaction();
        }

        public override void OnActionExecuted(HttpActionExecutedContext actionContext)
        {
            var context = IoC.CurrentNestedContainer.GetInstance<DatabaseContext>();
            context.CloseTransaction(actionContext.Exception);
        }
    }

0

आपको प्रत्येक सेव () ऑपरेशन के तुरंत बाद संदर्भ को निपटाना चाहिए। अन्यथा प्रत्येक बाद में सहेजने में अधिक समय लगेगा। मेरे पास एक परियोजना थी जिसने एक चक्र में जटिल डेटाबेस संस्थाओं को बनाया और बचाया। मेरे आश्चर्य के बाद, चक्र के अंदर "" (var ctx = new MyContext ()) {...} "का उपयोग करने के बाद ऑपरेशन तीन गुना तेज हो गया।

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.