एंटिटी फ्रेमवर्क से SqlException - नए लेनदेन की अनुमति नहीं है क्योंकि सत्र में अन्य थ्रेड चल रहे हैं


600

मुझे वर्तमान में यह त्रुटि मिल रही है:

System.Data.SqlClient.SqlException: नए लेन-देन की अनुमति नहीं है क्योंकि सत्र में अन्य थ्रेड चल रहे हैं।

इस कोड को चलाते समय:

public class ProductManager : IProductManager
{
    #region Declare Models
    private RivWorks.Model.Negotiation.RIV_Entities _dbRiv = RivWorks.Model.Stores.RivEntities(AppSettings.RivWorkEntities_connString);
    private RivWorks.Model.NegotiationAutos.RivFeedsEntities _dbFeed = RivWorks.Model.Stores.FeedEntities(AppSettings.FeedAutosEntities_connString);
    #endregion

    public IProduct GetProductById(Guid productId)
    {
        // Do a quick sync of the feeds...
        SyncFeeds();
        ...
        // get a product...
        ...
        return product;
    }

    private void SyncFeeds()
    {
        bool found = false;
        string feedSource = "AUTO";
        switch (feedSource) // companyFeedDetail.FeedSourceTable.ToUpper())
        {
            case "AUTO":
                var clientList = from a in _dbFeed.Client.Include("Auto") select a;
                foreach (RivWorks.Model.NegotiationAutos.Client client in clientList)
                {
                    var companyFeedDetailList = from a in _dbRiv.AutoNegotiationDetails where a.ClientID == client.ClientID select a;
                    foreach (RivWorks.Model.Negotiation.AutoNegotiationDetails companyFeedDetail in companyFeedDetailList)
                    {
                        if (companyFeedDetail.FeedSourceTable.ToUpper() == "AUTO")
                        {
                            var company = (from a in _dbRiv.Company.Include("Product") where a.CompanyId == companyFeedDetail.CompanyId select a).First();
                            foreach (RivWorks.Model.NegotiationAutos.Auto sourceProduct in client.Auto)
                            {
                                foreach (RivWorks.Model.Negotiation.Product targetProduct in company.Product)
                                {
                                    if (targetProduct.alternateProductID == sourceProduct.AutoID)
                                    {
                                        found = true;
                                        break;
                                    }
                                }
                                if (!found)
                                {
                                    var newProduct = new RivWorks.Model.Negotiation.Product();
                                    newProduct.alternateProductID = sourceProduct.AutoID;
                                    newProduct.isFromFeed = true;
                                    newProduct.isDeleted = false;
                                    newProduct.SKU = sourceProduct.StockNumber;
                                    company.Product.Add(newProduct);
                                }
                            }
                            _dbRiv.SaveChanges();  // ### THIS BREAKS ### //
                        }
                    }
                }
                break;
        }
    }
}

मॉडल # 1 - यह मॉडल हमारे देव सर्वर पर एक डेटाबेस में बैठता है। मॉडल # 1 http://content.screencast.com/users/Keith.Barrows/folders/Jing/media/bdb2b000-6e60-4af0-a7a1-2bb6b6b5d8bc1/Model1.png

मॉडल # 2 - यह मॉडल हमारे प्रोडक्ट सर्वर पर एक डेटाबेस में बैठता है और प्रत्येक दिन स्वचालित फीड द्वारा अपडेट किया जाता है। alt text http://content.screencast.com/users/Keith.Barrows/folders/Jing/media/4260259f-bce6-43d5-9d2a-017bd9a80d4/Model2.png

नोट - मॉडल # 1 में लाल परिक्रमा की गई वस्तुएं वे फ़ील्ड हैं जिनका मैं मॉडल # 2 में "मैप" करने के लिए उपयोग करता हूं। कृपया मॉडल # 2 में लाल हलकों को नजरअंदाज करें: यह एक और सवाल है जो मेरे पास था जो अब उत्तर दिया गया है।

नोट: मुझे अभी भी एक हटाए गए चेक में डालने की आवश्यकता है ताकि मैं इसे DB1 से हटा सकता हूं यदि यह हमारे क्लाइंट की इन्वेंट्री से बाहर चला गया है।

मैं केवल इस विशेष कोड के साथ करना चाहता हूं, DB1 में एक कंपनी को DB2 में एक ग्राहक के साथ जोड़ रहा हूं, DB2 से उनकी उत्पाद सूची प्राप्त करें और DB1 में इसे सम्मिलित करें यदि यह पहले से ही नहीं है। पहली बार के माध्यम से सूची का एक पूरा पुल होना चाहिए। हर बार इसे तब तक चलाया जाता है जब तक कि कुछ नहीं होना चाहिए जब तक कि रात में फ़ीड पर नई सूची न आए।

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


6
यह अब तक का सबसे विस्तृत प्रश्न है।

9
किसी को भी संग्रहीत प्रक्रियाओं की याद आती है?
डेविड

जवाबों:


690

बालों से बहुत खींचने के बाद मुझे पता चला कि foreachलूप अपराधी थे। ईएफ को कॉल करने के लिए क्या होना चाहिए, लेकिन इसे IList<T>उस लक्ष्य प्रकार में लौटाएं, फिर लूप ऑन करें IList<T>

उदाहरण:

IList<Client> clientList = from a in _dbFeed.Client.Include("Auto") select a;
foreach (RivWorks.Model.NegotiationAutos.Client client in clientList)
{
   var companyFeedDetailList = from a in _dbRiv.AutoNegotiationDetails where a.ClientID == client.ClientID select a;
    // ...
}

14
हाँ, इससे मुझे भी सिरदर्द हुआ। समस्या का पता चलने पर मैं अपनी कुर्सी से गिर गया! मैं समस्या के पीछे तकनीकी कारणों को समझता हूं, लेकिन यह सहज नहीं है और यह डेवलपर को "सफलता के गड्ढे" में गिरने में मदद नहीं कर रहा है । blogs.msdn.com/brada/archive/2003/10/02/50420। aspx
डॉक्टर जोन्स

9
क्या बड़े डेटासेट के लिए प्रदर्शन बुरा नहीं है? यदि आपके पास तालिका में लाखों रिकॉर्ड हैं। ToList () उन सभी को स्मृति में चूस लेगा। मैं इस समस्या में भाग रहा हूं और सोच रहा था कि क्या निम्नलिखित संभव होगा) a) इकाई को अलग करें b) एक नया ObjectContext बनाएं और इसमें अलग की गई इकाई को संलग्न करें। c) नई ObjectContext पर SaveChanges () कॉल करें) नई ObjectContext ई से इकाई को अलग करें) इसे वापस पुरानी ObjectContext में संलग्न करें
Abhijeet Patel

149
समस्या यह है कि SaveChangesजब आप अभी भी DB से परिणाम खींच रहे हैं तो आप कॉल नहीं कर सकते । इसलिए एक और उपाय यह है कि लूप पूरा होने के बाद बदलाव को बचाया जाए।
ड्रू नोक

4
काटे जाने के बाद भी मैंने इसे Microsoft Connect से जोड़ा: connect.microsoft.com/VisualStudio/feedback/details/612369/… इसे वोट करने के लिए स्वतंत्र महसूस करें।
इयान मर्सर

36
हमारे देवता परिणामों के बारे में सोचे बिना किसी LINQ क्वेरी में .ToList () को जोड़ देते हैं। यह पहली बार संलग्न होना चाहिए। टालस्ट () वास्तव में उपयोगी है!
मार्क

267

जैसा कि आपने पहले ही पहचान लिया है, आप foreachएक सक्रिय पाठक के माध्यम से डेटाबेस से अभी भी ड्राइंग के भीतर से नहीं बचा सकते हैं ।

बुलाना ToList()याToArray() छोटे डेटा सेट के लिए ठीक है, लेकिन जब आपके पास हजारों पंक्तियां होती हैं, तो आप बड़ी मात्रा में मेमोरी का उपभोग करेंगे।

चंक्स में पंक्तियों को लोड करना बेहतर है।

public static class EntityFrameworkUtil
{
    public static IEnumerable<T> QueryInChunksOf<T>(this IQueryable<T> queryable, int chunkSize)
    {
        return queryable.QueryChunksOfSize(chunkSize).SelectMany(chunk => chunk);
    }

    public static IEnumerable<T[]> QueryChunksOfSize<T>(this IQueryable<T> queryable, int chunkSize)
    {
        int chunkNumber = 0;
        while (true)
        {
            var query = (chunkNumber == 0)
                ? queryable 
                : queryable.Skip(chunkNumber * chunkSize);
            var chunk = query.Take(chunkSize).ToArray();
            if (chunk.Length == 0)
                yield break;
            yield return chunk;
            chunkNumber++;
        }
    }
}

उपरोक्त एक्सटेंशन विधियों को देखते हुए, आप अपनी क्वेरी इस तरह लिख सकते हैं:

foreach (var client in clientList.OrderBy(c => c.Id).QueryInChunksOf(100))
{
    // do stuff
    context.SaveChanges();
}

जिस क्वेरी ऑब्जेक्ट को आप इस विधि पर कहते हैं, उसे ऑर्डर करना होगा। ऐसा इसलिए है क्योंकि एंटिटी फ्रेमवर्क केवल IQueryable<T>.Skip(int)ऑर्डर किए गए प्रश्नों का समर्थन करता है , जो समझ में आता है जब आप विचार करते हैं कि विभिन्न श्रेणियों के लिए कई प्रश्नों को क्रम स्थिर होने की आवश्यकता होती है। यदि ऑर्डर करना आपके लिए महत्वपूर्ण नहीं है, तो प्राथमिक कुंजी द्वारा ऑर्डर करें क्योंकि यह एक क्लस्टर इंडेक्स होने की संभावना है।

यह संस्करण 100 के बैच में डेटाबेस को क्वेरी करेगा। ध्यान दें कि SaveChanges()प्रत्येक इकाई के लिए कहा जाता है।

यदि आप नाटकीय रूप से अपने थ्रूपुट में सुधार करना चाहते हैं, तो आपको SaveChanges()कम बार कॉल करना चाहिए । इसके बजाय कोड का उपयोग करें:

foreach (var chunk in clientList.OrderBy(c => c.Id).QueryChunksOfSize(100))
{
    foreach (var client in chunk)
    {
        // do stuff
    }
    context.SaveChanges();
}

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

और यह उस अपवाद के आसपास हो जाता है जो आप देख रहे थे।

संपादित करें I ने SQL Profiler चलाने के बाद इस प्रश्न पर दोबारा गौर किया और प्रदर्शन को बेहतर बनाने के लिए कुछ चीजों को अपडेट किया। जो कोई रुचि रखता है, उसके लिए यहां कुछ नमूना एसक्यूएल है जो दिखाता है कि डीबी द्वारा क्या बनाया गया है।

पहले लूप को कुछ भी छोड़ने की ज़रूरत नहीं है, इसलिए सरल है।

SELECT TOP (100)                     -- the chunk size 
[Extent1].[Id] AS [Id], 
[Extent1].[Name] AS [Name], 
FROM [dbo].[Clients] AS [Extent1]
ORDER BY [Extent1].[Id] ASC

बाद की कॉल के परिणाम के पिछले विखंडू को छोड़ने की आवश्यकता होती है, इसलिए row_numberनिम्न का उपयोग करता है :

SELECT TOP (100)                     -- the chunk size
[Extent1].[Id] AS [Id], 
[Extent1].[Name] AS [Name], 
FROM (
    SELECT [Extent1].[Id] AS [Id], [Extent1].[Name] AS [Name], row_number()
    OVER (ORDER BY [Extent1].[Id] ASC) AS [row_number]
    FROM [dbo].[Clients] AS [Extent1]
) AS [Extent1]
WHERE [Extent1].[row_number] > 100   -- the number of rows to skip
ORDER BY [Extent1].[Id] ASC

17
धन्यवाद। आपकी व्याख्या "उत्तर" के रूप में चिह्नित एक से अधिक उपयोगी थी।
वाग्नेर दा सिल्वा

1
यह भी खूब रही। बस एक चीज: यदि आप किसी कॉलम पर क्वेरी कर रहे हैं और उस कॉलम का मान अपडेट कर रहे हैं, तो आपको chunkNumber ++ का वेयर होना चाहिए; । मान लें कि आपके पास एक कॉलम "मॉडिफाइडडेट" है और आप क्वेरी कर रहे हैं। कहां (x => x.ModifiedDate! = Null), और फॉर्च्यूनर के अंत में आपने मॉडिफाइडडेट के लिए एक मान निर्धारित किया है। इस तरह आप आधे अभिलेखों का पुनरावृत्ति नहीं कर रहे हैं क्योंकि आधे अभिलेखों को छोड़ दिया जा रहा है।
अरविंद

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

मुझे लगता है कि यह काम करना चाहिए। var स्किप = 0; const int ले = 100; सूची <कर्मचारी> ईपीएस; जबकि ((ईपीएस = db.Employees.Skip (स्किप)) .Take (टेक) (.TLList ())। काउंट> 0) {स्किप + = ले; foreach (emps में var एम्पायर) {// Do here here}} मैं इसका उत्तर तैयार करूंगा लेकिन इसे नीचे दिए गए उत्तरों के ढेर के नीचे दफन किया जाएगा और यह इस प्रश्न से संबंधित है।
jwize

123

हमने अब कनेक्ट पर खोले गए बग पर एक आधिकारिक प्रतिक्रिया पोस्ट की है । हमारे द्वारा सुझाए गए वर्कअराउंड निम्नानुसार हैं:

यह त्रुटि SaveChanges () कॉल के दौरान एक अंतर्निहित लेनदेन बनाने के कारण एंटिटी फ्रेमवर्क के कारण है। त्रुटि के चारों ओर काम करने का सबसे अच्छा तरीका एक अलग पैटर्न का उपयोग करना है (यानी, पढ़ने के बीच में बचत नहीं करना) या स्पष्ट रूप से लेनदेन की घोषणा करके। यहां तीन संभावित उपाय दिए गए हैं:

// 1: Save after iteration (recommended approach in most cases)
using (var context = new MyContext())
{
    foreach (var person in context.People)
    {
        // Change to person
    }
    context.SaveChanges();
}

// 2: Declare an explicit transaction
using (var transaction = new TransactionScope())
{
    using (var context = new MyContext())
    {
        foreach (var person in context.People)
        {
            // Change to person
            context.SaveChanges();
        }
    }
    transaction.Complete();
}

// 3: Read rows ahead (Dangerous!)
using (var context = new MyContext())
{
    var people = context.People.ToList(); // Note that this forces the database
                                          // to evaluate the query immediately
                                          // and could be very bad for large tables.

    foreach (var person in people)
    {
        // Change to person
        context.SaveChanges();
    }
} 

6
यदि आप लेन-देन का मार्ग लेते हैं, तो TransactionScope में फेंकने से इसे ठीक नहीं किया जा सकता है - यदि आप जो कर रहे हैं तो टाइमआउट का विस्तार करना न भूलें, यदि आप एक लंबा समय ले सकते हैं - उदाहरण के लिए, यदि आप अंतःक्रियात्मक रूप से कोड बनाते हुए कोड डिबगिंग करेंगे DB कॉल। यहाँ कोड को एक घंटे में लेन-देन का समय बढ़ा दिया गया है: (var transaction = new TransactionScope (TransactionScopeOption.Required, new TimeSpan (1, 0, 0))) का उपयोग करके
क्रिस मॉसचिनि

मैं इस त्रुटि से टकरा गया हूँ जब मैंने पहली बार "ट्यूटोरियल पथ" से अपने वास्तविक उदाहरण में पचा लिया है! मेरे लिए, हालांकि, सरल समाधान, बचाओ बचाओ, बेहतर! (मुझे लगता है कि 99% बार ऐसा ही होता है, और केवल 1% ही वास्तव में जरूरी है कि डेटाबेस को इनसाइड लूप सेव करें)
स्पाइडरमैन

कुल। मैं इस त्रुटि से टकरा गया। दर्दनाक। दूसरा सुझाव मेरे सेवचेंज को लूप में ले जाने के साथ-साथ मेरे लिए एक आकर्षण की तरह काम करता है। मैंने सोचा कि लूप के बाहर परिवर्तनों को सहेजना बैच परिवर्तन के लिए बेहतर था। लेकिन ठीक है। मुझे नहीं लगता?! :(
श्री यंग

मेरे लिए काम नहीं किया .NET 4.5। जब TransactionScope का उपयोग किया गया तो मुझे निम्नलिखित त्रुटि मिली "अंतर्निहित प्रदाता EnlistTransaction पर विफल हो गया। {" भागीदार लेनदेन प्रबंधक ने दूरस्थ / नेटवर्क लेनदेन के लिए अपने समर्थन को अक्षम कर दिया है। (HRESULT से अपवाद: 0x8004D025) "}"। मैं अंत में पुनरावृत्ति के बाहर काम कर रहा हूँ।
दिगंत कुमार

TransactionScope का उपयोग करना खतरनाक है, क्योंकि पूरे लेन-देन के समय के लिए तालिका लॉक है।
माइकल फ्रीजिम

19

वास्तव में आप foreachEntity फ्रेमवर्क का उपयोग करके C # में लूप के अंदर परिवर्तन नहीं बचा सकते हैं ।

context.SaveChanges() विधि एक नियमित डेटाबेस सिस्टम (RDMS) पर एक कमिट की तरह काम करती है।

बस सभी परिवर्तन करें (जो कि एंटिटी फ्रेमवर्क कैश करेगा) और फिर SaveChanges()डेटाबेस कमिट कमांड की तरह लूप (इसके बाहर) के बाद एक बार कॉल करने पर उन सभी को बचाएं ।

यह काम करता है अगर आप एक ही बार में सभी परिवर्तनों को बचा सकते हैं।


2
मुझे लगा कि यहां "नियमित डेटाबेस सिस्टम (
आरडीएमएस

1
यह गलत लगता है, क्योंकि बार-बार SaveChanges को कॉल करना EF में 90% संदर्भों में ठीक है।
Pxtl

ऐसा लगता है जैसे बार-बार सेवचेंज को कॉल करना ठीक है, जब तक कि फ़ॉरबेक लूप एक डीबी एंटिटी पर निर्भर नहीं करता है।
kerbasaurus

1
अहा! प्रत्येक पाश के लिए अंदर संदर्भ लाओ! (pffft ... मैं क्या सोच रहा था? ..) धन्यवाद!
एडम कॉक्स

18

बस context.SaveChanges()अपने foreach(लूप) के अंत के बाद डाल दिया ।


यह बेहतर विकल्प है जो मुझे अपने मामले में फॉर्च्यूनर के अंदर बचाने के कारण पता चला
अल्मीडा

2
यह हमेशा एक विकल्प नहीं है।
17

9

हमेशा सूची के रूप में अपने चयन का उपयोग करें

उदाहरण के लिए:

var tempGroupOfFiles = Entities.Submited_Files.Where(r => r.FileStatusID == 10 && r.EventID == EventId).ToList();

फिर परिवर्तनों को सहेजते हुए संग्रह के माध्यम से लूप करें

 foreach (var item in tempGroupOfFiles)
             {
                 var itemToUpdate = item;
                 if (itemToUpdate != null)
                 {
                     itemToUpdate.FileStatusID = 8;
                     itemToUpdate.LastModifiedDate = DateTime.Now;
                 }
                 Entities.SaveChanges();

             }

1
यह अच्छा व्यवहार नहीं है। आपको SaveChanges को निष्पादित नहीं करना चाहिए जो अक्सर आपको आवश्यकता नहीं होती है, और आपको निश्चित रूप से "हमेशा अपने चयन को सूची के रूप में उपयोग नहीं करना चाहिए"
डायनेडो

@ डेंडरडो यह वास्तव में परिदृश्य पर निर्भर करता है। मेरे मामले में, मेरे पास 2 फॉरेक्स लूप हैं। बाहरी व्यक्ति के पास सूची के रूप में db क्वेरी थी। उदाहरण के लिए, यह हार्डवेयर उपकरणों को पीछे छोड़ता है। इनर फ़ॉरचेक प्रत्येक डिवाइस से कई डेटा पुनर्प्राप्त करता है। आवश्यकता के अनुसार, मुझे प्रत्येक डिवाइस को एक-एक करके पुनर्प्राप्त करने के बाद डेटा को डेटाबेस में सहेजने की आवश्यकता है। यह प्रक्रिया के अंत में सभी डेटा को सहेजने का विकल्प नहीं है। मुझे उसी त्रुटि का सामना करना पड़ा लेकिन mzonerz के समाधान ने काम किया।
jstuardo

@jstuardo बैचिंग के साथ भी?
डायनेडो

@ डेंडरडो मैं मानता हूं कि यह दार्शनिक स्तर पर एक अच्छा अभ्यास नहीं है। हालाँकि, कई स्थितियाँ मौजूद होती हैं जहाँ लूप के लिए कोड एक अन्य विधि को कॉल करता है (मान लें कि AddToLog () विधि) जिसमें स्थानीय रूप से db.SaveChanges () कॉल शामिल है। इस स्थिति में आप वास्तव में db.Save परिवर्तन पर कॉल को नियंत्रित नहीं कर सकते। इस मामले में, एक टोलिस्ट () या इसी तरह की संरचना का उपयोग करना mzonerz द्वारा सुझाए गए अनुसार काम करेगा। धन्यवाद!
ए। वर्मा

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

8

FYI करें: एक पुस्तक से और कुछ पंक्तियों को समायोजित किया गया क्योंकि इसका स्टिल मान्य है:

SaveChanges () पद्धति को लागू करने से एक लेनदेन शुरू होता है जो डेटाबेस में जारी सभी परिवर्तनों को स्वचालित रूप से रोल करता है यदि कोई अपवाद पूरा होने से पहले होता है; अन्यथा लेनदेन शुरू होता है। प्रत्येक इकाई अद्यतन या विलोपन के बाद विलोपन के बाद विधि को लागू करने के लिए आपको प्रलोभन दिया जा सकता है, खासकर जब आप अपडेट कर रहे हैं या भारी संख्या में संस्थाओं को हटा रहे हैं।

यदि आप सभी डेटा संसाधित होने से पहले SaveChanges () को लागू करने का प्रयास करते हैं, तो आपको "नया लेनदेन करने की अनुमति नहीं है क्योंकि सत्र में अन्य थ्रेड चल रहे हैं" अपवाद। अपवाद तब होता है क्योंकि SQL सर्वर कनेक्शन पर एक नया लेनदेन शुरू करने की अनुमति नहीं देता है जिसमें SqlDataReader खुला है, यहां तक ​​कि कनेक्शन स्ट्रिंग द्वारा सक्षम कई सक्रिय रिकॉर्ड सेट (MARS) के साथ भी (EF का डिफ़ॉल्ट कनेक्शन स्ट्रिंग MARS सक्षम करता है)

कभी-कभी यह समझने में बेहतर होता है कि चीजें क्यों हो रही हैं ;-)


1
इससे बचने का एक अच्छा तरीका यह है कि जब आपके पास एक पाठक एक दूसरे को खोलने के लिए खुला हो और दूसरे पाठक में उन कार्यों को रखा जाए। यह वह चीज है जिसकी आपको आवश्यकता तब पड़ सकती है जब आप इकाई ढांचे में मास्टर / विवरण अपडेट कर रहे हों। आप मास्टर रिकॉर्ड के लिए पहला कनेक्शन खोलें और दूसरा विवरण रिकॉर्ड के लिए। यदि आप केवल पढ़ रहे हैं तो कोई समस्या नहीं होनी चाहिए। अद्यतन करने के दौरान समस्याएँ होती हैं।
हरमन वान डेर ब्लूम

सहायक व्याख्या। आप सही हैं, यह समझना अच्छा है कि चीजें क्यों हो रही हैं।
डोवर मिलर


5

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


3
हाल ही में इसी तरह का मुद्दा था: मेरे मामले में कारण SELECTसंग्रहीत प्रक्रिया में बयान था जो खाली परिणाम सेट का उत्पादन करता था और यदि उस परिणाम सेट को पढ़ा नहीं गया था, तो SaveChangesइस अपवाद को फेंक दिया।
n0rd

एसपी से अपठित परिणाम के साथ एक ही बात, संकेत के लिए बहुत बहुत धन्यवाद)
पावेल

4

यहां एक और 2 विकल्प हैं जो आपको प्रत्येक लूप के लिए SaveChanges () को लागू करने की अनुमति देते हैं।

पहला विकल्प एक DBContext का उपयोग करके अपनी सूची ऑब्जेक्ट्स को पुन: व्यवस्थित करने के लिए उत्पन्न करना है, और फिर SaveChanges () पर कॉल करने के लिए एक दूसरा DBContext बनाएं। यहाँ एक उदाहरण है:

//Get your IQueryable list of objects from your main DBContext(db)    
IQueryable<Object> objects = db.Object.Where(whatever where clause you desire);

//Create a new DBContext outside of the foreach loop    
using (DBContext dbMod = new DBContext())
{   
    //Loop through the IQueryable       
    foreach (Object object in objects)
    {
        //Get the same object you are operating on in the foreach loop from the new DBContext(dbMod) using the objects id           
        Object objectMod = dbMod.Object.Find(object.id);

        //Make whatever changes you need on objectMod
        objectMod.RightNow = DateTime.Now;

        //Invoke SaveChanges() on the dbMod context         
        dbMod.SaveChanges()
    }
}

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

//Get the list of objects you want from your DBContext, and select just the Id's and create a list
List<int> Ids = db.Object.Where(enter where clause here)Select(m => m.Id).ToList();

var objects = Ids.Select(id => db.Objects.Find(id));

foreach (var object in objects)
{
    object.RightNow = DateTime.Now;
    db.SaveChanges()
}

यह एक बढ़िया विकल्प है, जिसके बारे में मैंने सोचा था और किया था, लेकिन इसे उभारने की जरूरत है। नोट: i) आप गणना करने योग्य के रूप में पुनरावृत्ति कर सकते हैं जो बहुत बड़े सेटों के लिए अच्छा है; ii) आप इतने सारे रिकॉर्ड लोड करने की समस्या से बचने के लिए NoTracking कमांड का उपयोग कर सकते हैं (यदि यह आपका परिदृश्य है); iii) मुझे वास्तव में प्राथमिक-कुंजी एकमात्र विकल्प पसंद है - यह बहुत स्मार्ट है क्योंकि आप मेमोरी में बहुत कम डेटा लोड कर रहे हैं, लेकिन आप संभावित गतिशील अंतर्निहित डेटासेट पर टेक / स्किप के साथ काम नहीं कर रहे हैं।
टोड

4

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

उदाहरण के लिए

    using (var context = new DatabaseContext())
    {
        ...
        using (var context1 = new DatabaseContext())
        {
            ...
               context1.SaveChanges();
        }                         
        //get id of inserted object from context1 and use is.   
      context.SaveChanges();
   }

2

तो परियोजना में मैं यह एक ही मुद्दा था समस्या में foreachया में समस्या नहीं थी.toList() यह वास्तव में AutoFac विन्यास में हम इस्तेमाल किया था। इससे कुछ अजीब स्थितियां पैदा हुईं, उपरोक्त त्रुटि थी, लेकिन अन्य समान त्रुटियों का एक गुच्छा भी फेंक दिया गया था।

यह हमारा फिक्स था: इसे बदल दिया गया:

container.RegisterType<DataContext>().As<DbContext>().InstancePerLifetimeScope();
container.RegisterType<DbFactory>().As<IDbFactory>().SingleInstance();
container.RegisterType<UnitOfWork>().As<IUnitOfWork>().InstancePerRequest();

सेवा:

container.RegisterType<DataContext>().As<DbContext>().As<DbContext>();
container.RegisterType<DbFactory>().As<IDbFactory>().As<IDbFactory>().InstancePerLifetimeScope();
container.RegisterType<UnitOfWork>().As<IUnitOfWork>().As<IUnitOfWork>();//.InstancePerRequest();

क्या आप सोच सकते हैं कि आपको क्या समस्या थी? आपने हर बार एक नया Dbcontext बनाकर इसे हल किया?
एरान ओत्ज़ाप

2

मुझे पता है कि यह एक पुराना सवाल है लेकिन मैंने आज इस त्रुटि का सामना किया।

और मैंने पाया कि, डेटाबेस टेबल ट्रिगर में त्रुटि होने पर यह त्रुटि डाली जा सकती है।

आपकी जानकारी के लिए, जब आप इस त्रुटि को प्राप्त करते हैं, तो आप अपनी तालिकाओं को भी ट्रिगर कर सकते हैं।


2

मुझे एक विशाल परिणाम को पढ़ने और तालिका में कुछ रिकॉर्ड अपडेट करने की आवश्यकता थी। मैंने ड्रू नोक के उत्तर में सुझाए अनुसार विखंडू का उपयोग करने की कोशिश की ।

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

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

सिफारिश प्रत्येक बैच के लिए आपके संदर्भ को फिर से बनाने के लिए है।

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

नीचे मेरे कोड से एक स्निपेट दिया गया है:

  public void ProcessContextByChunks ()
  {
        var tableName = "MyTable";
         var startTime = DateTime.Now;
        int i = 0;
         var minMaxIds = GetMinMaxIds();
        for (int fromKeyID= minMaxIds.From; fromKeyID <= minMaxIds.To; fromKeyID = fromKeyID+_chunkSize)
        {
            try
            {
                using (var context = InitContext())
                {   
                    var chunk = GetMyTableQuery(context).Where(r => (r.KeyID >= fromKeyID) && (r.KeyID < fromKeyID+ _chunkSize));
                    try
                    {
                        foreach (var row in chunk)
                        {
                            foundCount = UpdateRowIfNeeded(++i, row);
                        }
                        context.SaveChanges();
                    }
                    catch (Exception exc)
                    {
                        LogChunkException(i, exc);
                    }
                }
            }
            catch (Exception exc)
            {
                LogChunkException(i, exc);
            }
        }
        LogSummaryLine(tableName, i, foundCount, startTime);
    }

    private FromToRange<int> GetminMaxIds()
    {
        var minMaxIds = new FromToRange<int>();
        using (var context = InitContext())
        {
            var allRows = GetMyTableQuery(context);
            minMaxIds.From = allRows.Min(n => (int?)n.KeyID ?? 0);  
            minMaxIds.To = allRows.Max(n => (int?)n.KeyID ?? 0);
        }
        return minMaxIds;
    }

    private IQueryable<MyTable> GetMyTableQuery(MyEFContext context)
    {
        return context.MyTable;
    }

    private  MyEFContext InitContext()
    {
        var context = new MyEFContext();
        context.Database.Connection.ConnectionString = _connectionString;
        //context.Database.Log = SqlLog;
        return context;
    }

FromToRange से और गुणों के साथ एक सरल संरचना है।


मैं यह देखने में विफल रहा कि आप अपने संदर्भ को कैसे "नवीनीकृत" कर रहे हैं। ऐसा लग रहा है कि आप बस हर एक के लिए एक नया संदर्भ बना रहे हैं।
सनकैट २२

@ Suncat2000, आप सही हैं, संदर्भ एक छोटी-सी जीवित वस्तु होनी चाहिए stackoverflow.com/questions/43474112/…
माइकल फ्रीजिम

1

मैं भी उसी मुद्दे का सामना कर रहा था।

यहाँ कारण और समाधान है।

http://blogs.msdn.com/b/cbiyikoglu/archive/2006/11/21/mars-transactions-and-sql-error-3997-3988-or-3983.aspx

आवेषण, अपडेट जैसे डेटा हेरफेर कमांड को फायर करने से पहले सुनिश्चित करें, आपने पिछले सभी सक्रिय SQL पाठकों को बंद कर दिया है।

सबसे आम त्रुटि फ़ंक्शन है जो db और रिटर्न मान से डेटा पढ़ता है। उदाहरण के लिए जैसे कार्यों .RecordExist।

इस मामले में हम तुरंत फ़ंक्शन से लौटते हैं यदि हम रिकॉर्ड पाते हैं और पाठक को बंद करना भूल जाते हैं।


7
एंटिटी फ्रेमवर्क में "एक पाठक को बंद" का क्या मतलब है? कोई परिणाम में कोई दृश्यमान पाठक नहीं है जैसे var परिणाम = myDb.Customers में ग्राहक से जहां customer.Id == customerId ग्राहक का चयन करें; वापसी का परिणाम। FirstOrDefault ();
एंथनी

@Anthony जैसा कि अन्य उत्तर कहते हैं, यदि आप एक LINQ क्वेरी (IQueryable) पर गणना करने के लिए EF का उपयोग करते हैं, तो अंतर्निहित DataReader तब तक खुला रहेगा, जब तक कि अंतिम पंक्ति ओवररेटेड न हो जाए। लेकिन यद्यपि कनेक्शन-स्ट्रिंग में सक्षम करने के लिए MARS एक महत्वपूर्ण विशेषता है, लेकिन ओपी में समस्या अभी भी अकेले MARS के साथ हल नहीं हुई है। समस्या SaveChanges के लिए कोशिश कर रहा है, जबकि एक अंतर्निहित DataReader अभी भी खुला है।
टॉड

1

नीचे दिया गया कोड मेरे लिए काम करता है:

private pricecheckEntities _context = new pricecheckEntities();

...

private void resetpcheckedtoFalse()
{
    try
    {
        foreach (var product in _context.products)
        {
            product.pchecked = false;
            _context.products.Attach(product);
            _context.Entry(product).State = EntityState.Modified;
        }
        _context.SaveChanges();
    }
    catch (Exception extofException)
    {
        MessageBox.Show(extofException.ToString());

    }
    productsDataGrid.Items.Refresh();
}

2
एसओ में आपका स्वागत है! एक स्पष्टीकरण और / या यह बताने के लिए लिंक जोड़ने पर विचार करें कि यह आपके लिए क्यों काम करता है। कोड-ओनली उत्तरों को आमतौर पर SO के लिए अच्छी गुणवत्ता नहीं माना जाता है।
कोडमैजिक

1

मेरे मामले में, समस्या तब दिखाई दी जब मैंने EF के माध्यम से संग्रहीत प्रक्रिया को बुलाया और फिर बाद में SaveChanges ने इस अपवाद को फेंक दिया। समस्या प्रक्रिया को कॉल करने में थी, एन्यूमरेटर का निपटान नहीं किया गया था। मैंने निम्नलिखित तरीके से कोड तय किया:

public bool IsUserInRole(string username, string roleName, DataContext context)
{          
   var result = context.aspnet_UsersInRoles_IsUserInRoleEF("/", username, roleName);

   //using here solved the issue
   using (var en = result.GetEnumerator()) 
   {
     if (!en.MoveNext())
       throw new Exception("emty result of aspnet_UsersInRoles_IsUserInRoleEF");
     int? resultData = en.Current;

     return resultData == 1;//1 = success, see T-SQL for return codes
   }
}

1

हमने इस त्रुटि को देखना शुरू कर दिया " EF5 से EF6 की ओर पलायन करने के बाद सत्र में चलने वाले अन्य थ्रेड्स" की अनुमति नहीं है

Google हमें यहां लाया लेकिन हम SaveChanges()लूप के अंदर नहीं बुला रहे हैं । डीबी से रीडिंग फॉरेस्ट लूप के अंदर ObjectContext.ExecuteFunction का उपयोग करके एक संग्रहीत कार्यविधि को निष्पादित करते समय त्रुटियों को उठाया गया था।

ObjectContext.ExecuteFunction के लिए कोई भी कॉल लेनदेन में फ़ंक्शन को लपेटता है। लेनदेन की शुरुआत करते समय पहले से ही एक खुला पाठक त्रुटि का कारण बनता है।

निम्नलिखित विकल्प को सेट करके लेनदेन में एसपी को लपेटने को अक्षम करना संभव है।

_context.Configuration.EnsureTransactionsForFunctionsAndCommands = false;

EnsureTransactionsForFunctionsAndCommandsविकल्प का अपना लेन-देन बनाए बिना चलाने के लिए सपा की अनुमति देता है और त्रुटि नहीं रह गया है उठाया है।

DbContextConfiguration.EnsureTransactionsForFunctionsAndCommands संपत्ति


0

मुझे पार्टी के लिए बहुत देर हो चुकी है लेकिन आज मुझे उसी त्रुटि का सामना करना पड़ा और मैंने जो हल किया वह सरल था। मेरा परिदृश्य इस दिए गए कोड के समान था जिसे मैं नेस्टेड फॉर-प्रत्येक लूप के अंदर DB लेनदेन कर रहा था।

समस्या यह है कि एक एकल DB लेन-देन में प्रत्येक लूप की तुलना में थोड़ा अधिक समय लगता है, इसलिए एक बार पहले का लेनदेन पूरा नहीं होने पर नया कर्षण एक अपवाद को फेंक देता है, इसलिए समाधान प्रत्येक लूप में एक नई वस्तु बनाने के लिए है जहाँ आप एक db लेनदेन कर रहे हैं।

उपर्युक्त परिदृश्यों के लिए समाधान इस प्रकार होगा:

foreach (RivWorks.Model.Negotiation.AutoNegotiationDetails companyFeedDetail in companyFeedDetailList)
                {
private RivWorks.Model.Negotiation.RIV_Entities _dbRiv = RivWorks.Model.Stores.RivEntities(AppSettings.RivWorkEntities_connString);
                    if (companyFeedDetail.FeedSourceTable.ToUpper() == "AUTO")
                    {
                        var company = (from a in _dbRiv.Company.Include("Product") where a.CompanyId == companyFeedDetail.CompanyId select a).First();
                        foreach (RivWorks.Model.NegotiationAutos.Auto sourceProduct in client.Auto)
                        {
                            foreach (RivWorks.Model.Negotiation.Product targetProduct in company.Product)
                            {
                                if (targetProduct.alternateProductID == sourceProduct.AutoID)
                                {
                                    found = true;
                                    break;
                                }
                            }
                            if (!found)
                            {
                                var newProduct = new RivWorks.Model.Negotiation.Product();
                                newProduct.alternateProductID = sourceProduct.AutoID;
                                newProduct.isFromFeed = true;
                                newProduct.isDeleted = false;
                                newProduct.SKU = sourceProduct.StockNumber;
                                company.Product.Add(newProduct);
                            }
                        }
                        _dbRiv.SaveChanges();  // ### THIS BREAKS ### //
                    }
                }

0

मुझे थोड़ी देर हो गई है, लेकिन मुझे यह त्रुटि भी थी। मैंने यह जाँच कर समस्या का समाधान किया कि वे मान कहाँ हैं जहाँ अद्यतन किया जा रहा है।

मुझे पता चला कि मेरा प्रश्न गलत था और वहाँ जहाँ 250+ से अधिक संपादन लंबित थे। इसलिए मैंने अपनी क्वेरी सही की, और अब यह सही है।

तो मेरी स्थिति में: क्वेरी के परिणाम को डीबग करके त्रुटियों की क्वेरी की जाँच करें। इसके बाद क्वेरी को सही करें।

आशा है कि यह भविष्य की समस्याओं को हल करने में मदद करता है।

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