2010-02-18 16 views
8

Korzystanie z Entity Framework w .NET Chcę przechwycić listę elementów zwróconych z bazy danych i dokonać aktualizacji..NET EntityFramework: "Wystąpił błąd podczas rozpoczynania transakcji w połączeniu dostawcy.Więcej informacji na temat wewnętrznego wyjątku"

var qry = (from c in DBEntities.Customer select c); 
foreach (Object item in qry) 
{ 
    item.FirstName = .... 
    ... etc, other code here 
    DBEntities.SaveChanges(); 
} 

Według: http://social.msdn.microsoft.com/Forums/en/adodotnetentityframework/thread/8a337036-d288-48d4-80d4-89e5a51eddd9?ppud=4 S Hargroves sugeruje konwersji na IList i jest to rozwiązanie.

Nie próbowałem tego, jestem pewien, że to zadziała, ale nawet to działa, chcę wiedzieć, dlaczego nie mogę zaktualizować elementu podczas pętli? Dzieje się tak w moim lokalnym środowisku programistycznym, gdzie żaden inny użytkownik nie trafia do bazy danych.

Dzięki ...

+0

Co wewnętrzny wyjątek powiedzieć? Ogólnie rzecz biorąc, powinieneś wychwycić wyjątki, a następnie opublikować wynik "ex.ToString()". Powinieneś wtedy "rzucić"; ponownie, ponieważ "ex.ToString()" nie obsługuje wyjątku. –

Odpowiedz

3

Zgadzam się z przygotowaniem listy var. Następnie w swoim foreach, zamiast używać elementu Object w qry, użyj klienta klienta w qry. W tym scenariuszu naprawdę pracujesz z obiektami klientów, a nie tylko obiektami. W większości przypadków nie chcesz wywoływać SaveChanges() w foreach, ponieważ za każdym razem, gdy jest wykonywany, będzie wykonywał polecenie aktualizacji na serwerze. Jeśli zrobisz to po foreach, wykona on jedną partię wywołań do bazy danych i zrobi o wiele ładniej.

Moja sugerowana pseudokod wygląda coś jak

var customers = (from c in DBEntities.Customer select c).ToList<Customer>(); 
foreach (Customer customer item in customers) 
{ 
    customer.FirstName = .... 
    ... etc, other code here 

} 
DBEntities.SaveChanges(); 
0

Dzięki za zaleceniem na SavingChanges po pętli, nie pomyślałem o tym. Jednak muszę przetworzyć ponad 1 milion rekordów, to nie wszystko, ale jest trochę przetwarzania, które będzie wykonywane podczas każdej pętli. Dlatego jestem trochę zaniepokojony, że zajmie to sporo czasu, aby zapętlić 1 milion rekordów aktualizujących informacje, a następnie trochę poświęcić na opublikowanie zmian.

Wiem również, że EntityFramework prawdopodobnie nie jest najlepszym podejściem, ale jestem zainteresowany poznawaniem cech i ograniczeń Entity Framework.

Również w celu zwrócenia uwagi na listę var rzeczywiście zwraca obiekt typu ObjectQuery danego klienta. Tak więc pętla foreach może być zapisana w powyższy sposób, przy użyciu typu danych zmiennych klienta, nawet bez konwersji na listę.

Znowu moje pytanie, które chciałbym wiedzieć, to dlaczego nie mogę opublikować zmiany w tym samym obiekcie kontekstu (DBEntities) podczas pętli?

+2

nie publikujesz więcej informacji jako "odpowiedzi" na swoje pytanie, zamiast tego edytujesz oryginalne pytanie. – StarCub

11

Po zaktualizowaniu bazy danych za pomocą opcji SaveChanges w zapytaniu zapytanie jest unieważniane. Zestaw wyników mógł zostać zmieniony przez wykonaną aktualizację.

Za pomocą ToList uruchamiasz wykonywanie zapytania i przenosisz wszystkie wyniki z bazy danych do pamięci. Twoja lista w pamięci jest teraz konkretna i nie dotyczy już zapytania.

Ponieważ kwerendy obiektów używają IEnumerable, nie jest w porządku zrobić coś, co modyfikuje listę w foreach.

Wierzę również, ten kod nie powiedzie się z tych samych podstawowych powodów:

List<int> numbers = new List<int>() { 1,2,3,4,5,6}; 
foreach(var num in numbers) 
    numbers.Remove(num); //Invalidates the Enumerator being used in the foreach 
+0

Wywołanie "ToList" może być rozwiązaniem, ale co, jeśli zapytanie zawiera 50 000 rekordów lub więcej? Może to i może w końcu być zbyt dużą ilością danych do natychmiastowego wciągnięcia do pamięci. –

+0

Oryginalny kod miałby ten sam problem. O ile mi wiadomo, EF nie wykonuje żadnego "stronicowania" wyników przy wyliczaniu ObjectQuery. Możesz znaleźć sposób na podzielenie pierwotnego zapytania na partie lub skorzystaj z dwóch opcji kontekstowych, takich jak @Ron sugeruje – Tilendor

3

Ok ja wpadłem na ten sam problem. W tej chwili nie mam miliona rekordów tylko około 20 KB, ale tabela, którą chcę przetwarzać, przechowuje obrazy, więc przy przetwarzaniu tabeli na Listę trwa zbyt długo, Nawet w aplikacji na komputer.

Używam LinqToSql odkąd wyszedł i działa dobrze w LinqToSql, więc byłem trochę wkurzony, gdy widziałem, że nie działa w Entity. I to jest głupie, że Microsoft nie sprawił, że działa w Entity, ale tutaj jest praca. Zrób 2 obiekty kontekstowe. Jeden dla listy i jeden dla aktualizacji w następujący sposób.

entityList _imgList = new entityList(); 
entityList _imgSave = new entityList(); 

// Now the 1st time I did this I got the whole record like follows. 

    var _imgList = _imgList.Images.where(i=> i.NotProcessed == false); 
    foreach(Images _img_p in imgList) 
    { 
     if(something) 
     { 
      Images _img = _imgSave.Single(i=> i.ID == _img_p.ID); 
      _img.NotProcessed == true; 
      imgSave.SaveChanges(); 
     } 
    } 
imgList.dispose(); 
imgSave.dispose(); 


// After i verified this worked I figured why do I need to whole record to loop though so I changed it to just get the ID then process my loop as follows, and it works great. 

    var _imgIds = _imgList.Images.where(i=> i.NotProcessed == false).select(i=>i.ID); 
    foreach(long _imgID in imgList) 
    { 
     Images _img = _imgSave.Single(i=> i.ID == _imgID); 
     if(something) 
     { 
      _img.NotProcessed == true; 
      imgSave.SaveChanges(); 
     } 
    } 

imgList.dispose(); 
imgSave.dispose(); 

można zobaczyć więcej na moim blogu post (ASP.Net Help Blog)

+0

P.S. zapisywanie zmian po uruchomieniu pętli, ale gdy chcesz, aby zmiany zachodziły na żywo w momencie wyglądy, nie są – Ron

Powiązane problemy