Entity Framework SqlException - Oturumda çalışan başka iş parçacıkları olduğundan yeni işleme izin verilmiyor


600

Şu anda bu hatayı alıyorum:

System.Data.SqlClient.SqlException: Oturumda çalışan başka iş parçacıkları olduğundan yeni işleme izin verilmiyor.

bu kodu çalıştırırken:

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;
        }
    }
}

Model # 1 - Bu model Dev Server'ımızdaki bir veritabanında oturuyor. Model # 1 http://content.screencast.com/users/Keith.Barrows/folders/Jing/media/bdb2b000-6e60-4af0-a7a1-2bb6b05d8bc1/Model1.png

Model # 2 - Bu model Prod Sunucumuzdaki bir veritabanında bulunur ve her gün otomatik beslemelerle güncellenir. alt metin http://content.screencast.com/users/Keith.Barrows/folders/Jing/media/4260259f-bce6-43d5-9d2a-017bd9a980d4/Model2.png

Not - Model # 1'deki kırmızı daire içine alınmış öğeler Model # 2 ile "eşleştirmek" için kullandığım alanlardır. Lütfen Model # 2'deki kırmızı daireleri göz ardı edin: bu, şimdi yanıtlanan başka bir sorudan.

Not: Hala bir isDeleted çek koymak gerekir, böylece istemci envanter dışına gitti, DB1 yumuşak silebilirsiniz.

Tek yapmak istediğim, bu özel kod ile DB1'de bir şirkete DB2'de bir istemci bağlamak, DB2'den ürün listesini almak ve henüz orada değilse DB1'e INSERT. İlk kez envanter tam bir çekme olmalıdır. Her seferinde orada çalıştırıldığında, gece boyunca yeni envanter gelmedikçe hiçbir şey olmamalıdır.

Yani büyük soru - nasıl alıyorum işlem hatası çözmek için? Her seferinde döngülerden bağlamımı bırakıp yeniden oluşturmam gerekiyor mu (bana mantıklı değil)?


6
Bu şimdiye kadar gördüğüm en ayrıntılı soru.

9
Henüz saklanan prosedürleri özleyen var mı?
David

Yanıtlar:


690

Çok fazla saç çıkardıktan sonra foreachilmeklerin suçlu olduğunu keşfettim . Gerçekleşmesi gereken şey EF'yi aramak, ancak IList<T>o hedef türden birine geri döndürmek ve ardındanIList<T> .

Misal:

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
Evet, bu da başım ağrıyor. Sorunu bulduğumda neredeyse sandalyemden düştüm! Sorunun arkasındaki teknik nedenleri anlıyorum, ancak bu sezgisel değil ve geliştiricinin "başarı çukuru" na
Doctor Jones

9
Büyük veri kümelerinin performansı için kötü değil mi? Tabloda milyonlarca kayıt varsa. ToList () hepsini hafızaya emer. Bu sorunla karşılaşıyorum ve aşağıdakilerin mümkün olup olmadığını merak ediyordum a) Varlığı ayırın b) Yeni bir ObjectContext oluşturun ve ayrılmış varlığa ekleyin. c) Yeni ObjectContext üzerinde SaveChanges () öğesini çağırın d) Varlığı yeni ObjectContext'ten ayırın e) Eski ObjectContext'e geri ekleyin
Abhijeet Patel

149
Sorun, SaveChangeshala DB'den sonuç alırken arayamamanızdır. Bu nedenle başka bir çözüm, döngü tamamlandıktan sonra değişiklikleri kaydetmektir.
Drew Noakes

4
Ayrıca ısırıldıktan sonra bunu Microsoft Connect'e ekledim: connect.microsoft.com/VisualStudio/feedback/details/612369/… Oy vermek için çekinmeyin.
Ian Mercer

36
Geliştiricilerimiz, sonuçları düşünmeden herhangi bir LINQ sorgusuna .ToList () ekleme eğilimindedir. Bu ilk kez .ToList () eklemek gerçekten yararlı olmalı!
Marc

267

Daha önce tanımladığınız gibi, foreach aktif bir okuyucu aracılığıyla veritabanından hala çizim yapan .

Sesli arama ToList()veyaToArray() küçük veri setleri için iyi, ama satırların binlerce olduğu zaman, bellek büyük miktarda tüketen edilecektir.

Satırları yığınlara yüklemek daha iyidir.

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++;
        }
    }
}

Yukarıdaki uzantı yöntemleri göz önüne alındığında, sorgunuzu şöyle yazabilirsiniz:

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

Bu yöntemi çağırdığınız sorgulanabilir nesne sipariş edilmelidir. Bunun nedeni, Entity Framework'ün yalnızca IQueryable<T>.Skip(int)sıralı sorguları desteklemesidir ; bu, farklı aralıklar için birden çok sorgunun, siparişin kararlı olmasını gerektirdiğini düşündüğünüzde mantıklıdır. Sıralama sizin için önemli değilse, kümelenmiş bir dizine sahip olması muhtemel birincil anahtarla sipariş verin.

Bu sürüm, veritabanını 100'lük gruplar halinde sorgulayacaktır SaveChanges(). Her varlık için çağrıldığını unutmayın .

Veriminizi önemli ölçüde iyileştirmek istiyorsanız, SaveChanges()daha az sıklıkta aramalısınız . Bunun yerine şu kodu kullanın:

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

Bu, 100 kat daha az veritabanı güncelleme çağrısı ile sonuçlanır. Tabii ki bu çağrıların her birinin tamamlanması daha uzun sürüyor, ancak yine de sonunda öne çıkıyorsunuz. Kilometreniz değişebilir, ancak bu benim için daha hızlıydı.

Ve gördüğünüz istisnayı aşar.

EDIT SQL Profiler'ı çalıştırdıktan sonra bu soruyu tekrar gözden geçirdim ve performansı artırmak için birkaç şeyi güncelledim. İlgilenen herkes için, burada DB tarafından oluşturulanları gösteren bazı örnek SQL'ler bulunmaktadır.

İlk döngünün hiçbir şeyi atlaması gerekmez, bu yüzden daha basittir.

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

Sonraki aramaların önceki sonuç gruplarını atlaması gerekir, bu nedenle aşağıdakilerin kullanımını sağlar 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
Teşekkürler. Açıklamanız "Cevaplandı" olarak işaretlenmiş olandan çok daha yararlı oldu.
Wagner da Silva

1
Bu harika. sadece bir şey: Bir sütunda sorgulama ve bu sütunun değerini güncelleme, chunkNumber ++ 'ın eşya olması gerekir; . Diyelim ki "ModifiedDate" sütununuz var ve .Where (x => x.ModifiedDate! = Null) sorgusunu sorguladınız ve foreach'un sonunda ModifiedDate için bir değer ayarladınız. Bu şekilde kayıtların yarısını yinelemezsiniz çünkü kayıtların yarısı atlanır.
Arvand

Ne yazık ki, büyük veri kümelerinde OutofMemoryException - Varlık çerçevesi geniş veri kümesinde, bellek istisnası dışında bir açıklama göreceksiniz . Entity Framework SqlException
Michael Freidgeim

Bunun işe yarayacağını düşünüyorum. var atlama = 0; const int almak = 100; <Employee> emps listesini; while ((emps = db.Employees.Skip (atla) .Take (al) .ToList ()). Sayım> 0) {atla + = al; foreach (emps'de var emp) {// Burada bir şeyler yapın}} Bunu bir cevap formüle ederdim, ancak aşağıdaki cevap yığınlarının altına gömülür ve bu soru ile ilgilidir.
jwize

123

Şimdi Connect'te açılan hataya resmi bir yanıt gönderdik . Önerdiğimiz geçici çözümler şunlardır:

Bu hata, Entity Framework'ün SaveChanges () çağrısı sırasında örtük bir işlem oluşturmasından kaynaklanır. Hatayı geçici olarak çözmenin en iyi yolu farklı bir desen kullanmaktır (yani, okuma ortasındayken kaydetmemek) veya açıkça bir işlemi bildirmektir. İşte üç olası çözüm:

// 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
İşlem yolunu izlerseniz, sadece bir TransactionScope'a atmak sorunu çözmeyebilir - yaptığınız şeyin uzun sürebileceği durumlarda Zaman Aşımı süresini uzatmayı unutmayın - örneğin, kodu oluşturan kodda etkileşimli olarak hata ayıklayacaksanız DB çağrısı. İşlem zaman aşımı süresini bir saate uzatan kod: kullanma (var transaction = new TransactionScope (TransactionScopeOption.Required, yeni TimeSpan (1, 0, 0)))
Chris Moschini

Ben ilk kez "öğretici yol" dan gerçek bir örnek haline kazılmış bu hataya çarptı! Ancak benim için daha basit çözüm, KESİNTEN SONRA KAYDET, daha iyi! (Ben bence bu durumun% 99'u ve sadece% 1 gerçekten döngü içinde bir veritabanı gerçekleştirmek GEREKİR)
örümcek adam

Brüt. Bu hatayla karşılaştım. Çok kötü. 2 öneri benim SaveChanges döngü içine hareket ile birlikte benim için bir cazibe gibi çalıştı. Döngünün dışında değişiklikleri kaydetmenin toplu değişiklikler için daha iyi olduğunu düşündüm. Ama tamam. Sanırım hayır?! :(
Bay Young

Benim için işe yaramadı .NET 4.5. TransactionScope kullanıldığında "EnlistTransaction üzerinde temel sağlayıcı başarısız oldu." Hata iletisini aldım. {"Ortak işlem yöneticisi uzak / ağ işlemleri için desteğini devre dışı bıraktı. (HRESULT istisnası: 0x8004D025) "}". Sonunda işi yineleme dışında yapıyorum.
Diganta Kumar

TransactionScope kullanmak tehlikelidir, çünkü tablo tüm işlem boyunca kilitlidir.
Michael Freidgeim

19

Aslında, foreachEntity Framework kullanarak C # 'da bir döngü içindeki değişiklikleri kaydedemezsiniz .

context.SaveChanges() yöntemi normal bir veritabanı sistemi (RDMS) üzerinde işlem yapar.

Tüm değişiklikleri (Entity Framework'ün önbelleğe alacağı) yapın ve bir SaveChanges()döngü tamamladıktan sonra (bunun dışında) bir veritabanı kaydetme komutu gibi hepsini bir kerede kaydedin .

Bu, tüm değişiklikleri bir kerede kaydedebiliyorsanız kullanılabilir.


2
Burada "düzenli veritabanı sistemi (RDMS)" görmek ilginç olduğunu düşündüm
Dinerdo

1
EF'de bağlamların% 90'ında art arda SaveChanges çağrısı yapmak iyi olduğu için bu yanlış görünüyor.
Pxtl

Foreach döngüsü bir db Varlığı üzerinden yinelenmediği sürece, SaveChanges'i tekrar tekrar çağırmak iyi görünüyor.
kerbasaurus

1
Aha! Her döngü için bağlam getirin! (pffft ... ne düşünüyordum? ..) Teşekkürler!
Adam Cox

18

Sadece (döngü) context.SaveChanges()bittikten sonra koy foreach.


Bu benim foreach içinde foreach kurtarmak nedeniyle öğrendim daha iyi bir seçenek
Almeida

2
Bu her zaman bir seçenek değildir.
Pxtl

9

Seçiminizi Her Zaman Liste Olarak Kullan

Örneğin:

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

Ardından değişiklikleri kaydederken Koleksiyonun içinden geçin

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

             }

1
Bu hiç de iyi bir uygulama değil. SaveChanges'i gerek duymuyorsanız sık sık çalıştırmamalısınız ve kesinlikle "Seçiminizi her zaman Liste olarak kullanmamalısınız"
Dinerdo

@Dinerdo gerçekten senaryoya bağlı. Benim durumumda, 2 foreach döngüm var. Dış bir liste olarak db sorgusu vardı. Örneğin, bu foreach donanım aygıtlarını geçer. İç foreach her aygıttan birkaç veri alır. Gereksinime göre, her cihazdan birer birer alındıktan sonra veritabanına veri kaydetmek gerekir. İşlemin sonunda tüm verileri kaydetmek bir seçenek değildir. Aynı hatayla karşılaştım ama mzonerz çözümü işe yaradı.
jstuardo

@jstuardo Parti ile bile mi?
Dinerdo

@Dinerdo Felsefi düzeyde iyi bir uygulama olmadığını kabul ediyorum. Bununla birlikte, for döngüsünün içinde kodun yerel olarak db.SaveChanges () çağrısını içeren başka bir yöntemi (diyelim ki bir AddToLog () yöntemi) çağırdığı durumlarda vardır. Bu durumda db çağrısını gerçekten kontrol edemezsiniz. Bu durumda, bir ToList () veya benzer bir yapı kullanmak mzonerz tarafından önerilen şekilde çalışır. Teşekkürler!
A. Varma

Pratikte, bu size yardımcı olacağından daha fazla zarar verecektir. Söylediklerimin yanında duruyorum - ToList () kesinlikle her zaman kullanılmamalıdır ve her bir öğeden sonra değişiklikleri kaydetmek, yüksek performanslı bir uygulamada mümkün olan her yerde kaçınılması gereken bir şeydir. Bu geçici bir düzeltme IMO olacaktır. Hangi günlük yöntemine sahip olursanız olun, ideal olarak arabelleğe alma özelliğinden yararlanmalıdır.
Dinerdo

8

Bilginize: Bir kitaptan ve stil geçerli olduğu için ayarlanan bazı satırlardan:

SaveChanges () yöntemi çağrıldığında, yineleme tamamlanmadan önce bir özel durum oluşursa veritabanında kalıcı olarak yapılan tüm değişiklikleri otomatik olarak geri alan bir işlem başlatılır; aksi takdirde işlem yapılır. Yöntemi, yineleme tamamlandıktan sonra değil, özellikle çok sayıda varlığı güncelleştirirken veya silerken, her varlık güncellemesinden veya silmeden sonra uygulamak cazip olabilir.

Tüm veriler işlenmeden önce SaveChanges () işlevini çağırmayı denerseniz, "Oturumda çalışan başka evreler olduğundan yeni işleme izin verilmiyor" özel durumu oluşuyor. Özel durum, SQL Server, bağlantı dizesi tarafından etkinleştirilen Birden Çok Etkin Kayıt Kümesi (MARS) etkinleştirilmiş olsa bile, SqlDataReader açık bir bağlantıda yeni bir işlem başlatmaya izin vermediği için oluşur (EF'in varsayılan bağlantı dizesi MARS'yi etkinleştirir)

Bazen olayların neden olduğunu anlamak daha iyidir ;-)


1
Bundan kaçınmanın iyi bir yolu, ikinci bir tane açmak için açık bir okuyucunuz olması ve bu işlemleri ikinci okuyucuya koymaktır. Bu, varlık çerçevesinde ayrıntıları / ayrıntıları güncellerken ihtiyaç duyacağınız bir şeydir. Ana kayıt için ilk bağlantıyı ve ayrıntı kayıtları için ikinci bağlantıyı açarsınız. sadece okuyorsanız sorun olmamalıdır. sorunlar güncelleme sırasında oluşur.
Herman Van Der Blom

Yararlı açıklama. haklısın, neden olayların olduğunu anlamak güzel.
Dov Miller

8

Sorgulanabilir listelerinizi .ToList () haline getirmek ve iyi çalışmalıdır.


1
Lütfen yalnızca bir çözüm göndermek yerine bir örnek verin.
Ronnie Oosting

5

Aynı sorunu alıyordum ama farklı bir durumda. Bir liste kutusundaki öğelerin bir listesi vardı. Kullanıcı bir öğeyi tıklayıp sil'i seçebilir, ancak öğeyi silmek için çok fazla mantık olduğu için öğeyi silmek için saklı bir proc kullanıyorum. Saklı proc çağırdığınızda silme iyi çalışır ancak SaveChanges için gelecekteki herhangi bir çağrı hataya neden olur. Benim çözümüm EF dışında depolanan proc çağırmak oldu ve bu iyi çalıştı. Bir sebepten ötürü, EF'i bir şeyler yapmanın yolunu kullanarak kayıtlı proc'u çağırdığımda bir şeyi açık bırakır.


3
Son zamanlarda benzer bir sorun vardı: Benim durumumun nedeni, SELECTboş sonuç kümesi üreten saklı yordamdaki deyimdi ve bu sonuç kümesi okunmadıysa, SaveChangesbu istisnayı attı.
n0rd

SP okunmamış sonuçla aynı şey, ipucu için çok teşekkürler)
Pavel K

4

Her döngü için bir SaveChanges'i () çağırmanıza izin veren 2 seçenek daha vardır.

İlk seçenek, yinelenecek liste nesnelerinizi oluşturmak için bir DBContext kullanmak ve ardından SaveChanges () öğesini çağırmak için ikinci bir DBContext oluşturmaktır. İşte bir örnek:

//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()
    }
}

İkinci seçenek, DBContext'den veritabanı nesnelerinin bir listesini almak, ancak yalnızca kimlikleri seçmektir. Daha sonra id (muhtemelen int) listesini tekrarlayın ve her int'e karşılık gelen nesneyi alın ve SaveChanges () öğesini bu şekilde çağırın. Bu yöntemin arkasındaki fikir, tamsayıların büyük bir listesini kapmaktır, çok daha verimli olup, db nesnelerinin büyük bir listesini alıp tüm nesne üzerinde .ToList () öğesini çağırır. İşte bu yöntemin bir örneği:

//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()
}

Bu, düşündüğüm ve yaptığım harika bir alternatif, ancak bunun kaldırılması gerekiyor. Not: i) çok büyük setler için iyi olan numaralandırılabilir olarak tekrarlayabilirsiniz; ii) Çok fazla kayıt yüklemeyle ilgili sorunlardan kaçınmak için NoTracking komutunu kullanabilirsiniz (senaryonuz buysa); iii) Sadece birincil anahtar seçeneğini de seviyorum - bu çok akıllı çünkü belleğe çok daha az veri yüklüyorsunuz, ancak potansiyel olarak dinamik bir temel veri kümesinde Take / Skip ile uğraşmıyorsunuz.
Todd

4

Foreach nedeniyle bu hatayı alıyorsanız ve gerçekten bir varlığı ilk önce döngü içinde kaydetmeniz ve oluşturulmuş kimliği döngüde daha fazla kullanmanız gerekiyorsa, benim durumumda olduğu gibi, en kolay çözüm, kimliği döndürecek ve kullanacak varlık eklemek için başka bir DBContext kullanmaktır. dış bağlamda bu kimlik

Örneğin

    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

Yani projede de aynı sorunla karşılaşmıştım, problem içinde değildi foreachya da .toList()aslında kullandığımız AutoFac konfigürasyonundaydı. Bu, yukarıdaki hatanın atıldığı ancak aynı zamanda bir dizi eşdeğer hatanın atıldığı bazı garip durumlar yarattı.

Bu bizim fikrimizdi: Bunu değiştirdik:

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

Kime:

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

Sorunun ne olduğunu düşündüğünüzü açıklayabilir misiniz? her seferinde yeni bir Dbcontext oluşturarak çözdünüz mü?
eran otzap

2

Eski bir soru olduğunu biliyorum ama bugün bu hatayla karşılaştım.

ve bir veritabanı tablosu tetikleyici bir hata aldığında bu hata atılabilir bulundu.

bilgileriniz için bu hatayı aldığınızda tablo tetikleyicilerinizi de kontrol edebilirsiniz.


2

Ben büyük bir ResultSet okumak ve tablodaki bazı kayıtları güncellemek gerekiyordu. Önerildiği gibi kullanılması parçalar çalıştı Drew Noakes 'ın cevabı .

Ne yazık ki 50000 kayıttan sonra OutofMemoryException aldım. Cevap Varlık çerçeve büyük veri kümesi, bellek dışı istisna açıklıyor,

EF, değişiklik tespiti için kullanılan verilerin ikinci bir kopyasını oluşturur (böylece veritabanındaki değişiklikleri devam ettirebilir). EF, bağlamın ömrü boyunca bu ikinci seti tutar ve bu set sizi hafızanızdan çıkarır.

Öneri, her bir toplu iş için bağlamınızı yeniden oluşturmanızdır.

Bu yüzden birincil anahtarın Minimal ve Maximum değerlerini aldım - tabloların otomatik artımlı tamsayılar olarak birincil anahtarları var. Parça içeriği işlendikten sonra belleği kapatır ve serbest bırakır. Bellek kullanımının artmamasını sağlar.

Kodumdan bir kod parçası aşağıdadır:

  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 , From ve To özelliklerine sahip basit bir yapıdır.


Bağlamınızı nasıl "yenilediğinizi" göremedim. Her yığın için yeni bir bağlam oluşturduğunuz anlaşılıyor.
Suncat2000

@ Suncat2000, haklısınız, bağlam kısa ömürlü bir nesne olmalıdır stackoverflow.com/questions/43474112/…
Michael Freidgeim

1

Ben de aynı sorunla karşı karşıyaydım.

İşte nedeni ve çözümü.

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

Ekler, güncellemeler gibi veri işleme komutlarını tetiklemeden önce, önceki tüm etkin SQL okuyucularını kapattığınızdan emin olun.

En yaygın hata, db ve return değerlerinden veri okuyan işlevlerdir. Örneğin, isRecordExist gibi işlevler için.

Bu durumda, kaydı bulursak ve hemen okuyucuyu kapatmayı unutursak işlevden geri döneriz.


7
Entity Framework'te "bir okuyucuyu kapat" ne anlama geliyor? Bir sorguda var results = myDb'deki müşterilerden görünür bir okuyucu yoktur.Müşteriler burada customer.Id == customerId müşteri seç; sonucu döndür.FirstOrDefault ();
Anthony

@Anthony Diğer yanıtların söylediği gibi, EF'i bir LINQ sorgusu (IQueryable) üzerinde numaralandırmak için kullanırsanız, temel DataReader son satır tekrarlanana kadar açık kalır. Ancak MARS, bir bağlantı dizgisinde etkinleştirmek için önemli bir özellik olmasına rağmen, OP'deki sorun hala sadece MARS ile çözülmemiştir. Altta yatan bir DataReader açıkken sorun SaveChanges'i çalıştırmaya çalışıyor.
Todd

1

Aşağıdaki kod benim için çalışıyor:

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 hoş geldiniz! Bunun sizin için neden işe yaradığını açıklayan bir açıklama ve / veya bağlantılar eklemeyi düşünün . Yalnızca kod yanıtları SO için iyi kalitede değildir.
2014'te codeMagic

1

Benim durumumda, EF aracılığıyla Saklı Yordam çağırdığımda ve daha sonra SaveChanges bu özel durumu atarken sorun ortaya çıktı. Sorun prosedürü çağırmaktı, numaralandırıcı imha edilmedi. Aşağıdaki şekilde kodu sabit:

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'ten EF6'ya geçtikten sonra "Oturumda çalışan başka evreler olduğundan yeni işleme izin verilmiyor" hatasını görmeye başladık.

Google bizi buraya getirdi, ancak SaveChanges()döngü içinde arama yapmıyoruz . DB bir foreach döngüsü okuma içinde ObjectContext.ExecuteFunction kullanılarak saklı yordam yürütülürken hatalar oluştu.

ObjectContext.ExecuteFunction öğesine yapılan her çağrı, işlevi bir işlemde sarar. Zaten açık bir okuyucu varken bir işleme başlamak hataya neden olur.

Aşağıdaki seçeneği ayarlayarak bir işlemde SP'nin kaydırılmasını devre dışı bırakmak mümkündür.

_context.Configuration.EnsureTransactionsForFunctionsAndCommands = false;

Bu EnsureTransactionsForFunctionsAndCommandsseçenek, SP'nin kendi işlemini oluşturmadan çalışmasına izin verir ve hata artık yükseltilmez.

DbContextConfiguration.EnsureTransactionsForFunctionsAndCommands Özelliği


0

Partiye çok geç kaldım ama bugün aynı hatayla karşılaştım ve nasıl çözdüğüm basitti. Benim senaryo, her döngüler için iç içe DB işlemleri yapıyordum verilen bu kod benzerdi.

Tek bir DB işleminin her döngüden biraz daha uzun sürmesi nedeniyle sorun, bu nedenle önceki işlem tamamlandıktan sonra yeni çekiş bir istisna atar, böylece çözüm for-each döngüsünde yeni bir nesne oluşturmaktır Burada bir db işlemi yapıyorsunuz.

Yukarıda belirtilen senaryolar için çözüm şöyle olacaktır:

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

Biraz geç kaldım ama bu hatayı da aldım. Ben nerede nerede değerleri nerede güncelleme kontrol ederek sorunu çözdü.

Sorgumun yanlış olduğunu ve 250'den fazla düzenlemenin beklemede olduğunu öğrendim. Bu yüzden sorgumu düzelttim ve şimdi doğru çalışıyor.

Bu yüzden benim durumumda: Sorgunun döndürdüğü sonuç üzerinde hata ayıklayarak sorguyu hatalara karşı denetleyin. Bundan sonra sorguyu düzeltin.

Umarım bu gelecekteki sorunların çözülmesine yardımcı olur.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.