2010-06-29 10 views
5

Podczas korzystania z SqlConnection ważne jest, aby zawsze zamykać je podczas używania - przez .Close() lub umieszczając SqlConnection w "użyciu". Na szczęście ludzie, w tym ja, zapomnielibyśmy o tym i to jest miejsce, w którym od czasu do czasu zbieracze śmieci ratują mnie, dopóki nie zapomniałem zbyt wiele razy zamknąć połączeń lub liczba osób korzystających z aplikacji rośnie.Wykrywanie, czy wywoływacz został wywołany (.Net)

Chciałbym wiedzieć, jeśli to możliwe, jak wykryć, czy śmieciarz usunął SqlConnection, ponieważ uznał, że nie był już używany lub czy SqlConnection został zamknięty we właściwy sposób.

Innym sposobem może być dziedziczenie SqlConnection i umieszczenie licznika czasu na jego inicjalizatorze oraz sprawdzenie, ile czasu upłynęło, zanim połączenie zostanie zamknięte podczas usuwania klasy. Nie lubię zegarów, ale pomysł pojawił się podczas pisania tego.

Może jest trzeci, a nawet mądrzejszy sposób na to wszystko ... Co byś polecił?

+0

'albo .close() lub umieszczając SqlConnection w "użyciu" '** NO NO NO! ** .Close() nie jest wystarczająco dobre samo w sobie. Taki jest sens konstruktu 'using': zbyt wiele osób zapomina również, że twój .Close() _must_ powinien znajdować się w bloku finally. –

Odpowiedz

2

Jedna zasada mówi: "Jeśli musisz pomyśleć o śmieciu, prawdopodobnie robisz coś nie tak". (Oczywiście są też inne kciuki ...)

Wydaje mi się, że zapewnienie, że połączenia są zamykane jawnie lub za pomocą bloków using i finally jest najlepszą drogą.

Oczywiście, już rozumiesz te techniki ... więc może potrzebujesz tylko jednego kodu i możliwego refaktora.

0

Jeśli zapomnisz wyrzucić, obiekt zostanie sfinalizowany. Nie ma sposobu, aby kontrolować czas, w którym to się dzieje, i nie ma również sposobu, aby wiedzieć, czy obiekt jest sfinalizowany. Należy utworzyć osobny wątek w celu sfinalizowania obiektów, co spowalnia aplikację. Dlatego chcesz się pozbyć. We wszystkich klasach w ramach, które implementują IDisposable, wywoływane jest wywołanie GC.SuppressFinalize, więc obiekt nie jest sfinalizowany.

Nie można kontrolować tego zachowania. Jeśli Twój obiekt nie jest już używany, zostanie on automatycznie zebrany. Wszystko, co możesz zrobić, aby to zatrzymać, to zadzwoń do GC.SuppressFinalize, ale nie poleciłbym tego, ponieważ gdybyś wtedy zapomniał, zostałbyś wkręcony na całe życie.

Można jednak utworzyć klasę opakowania (nie jest to klasa podrzędna), której używa się w kodzie, który udostępnia kilka prostych metod, które zawsze wywołują Dispose. W przeciwnym razie po prostu sprawdź naprawdę dobrze.

+0

Powiedziałbym, że "zazwyczaj będzie sfinalizowane". Sytuacje mogą łatwo powstać, gdy brak "wyrzucenia" obiektu związanego z czasem statycznym uniemożliwi temu przedmiotowi, * lub cokolwiek, do czego ma bezpośrednie lub pośrednie odniesienie *, od * kiedykolwiek * zostanie zakwalifikowany do zbierania śmieci. – supercat

4

Ponieważ SqlConnection jest zapieczętowany, nie będzie można go dziedziczyć. (I nie sądzę, że jest to dobry pomysł - jeśli było to możliwe, prawdopodobnie powinieneś dodać swój kod w Dispose (false), ponieważ tak właśnie nazywa się finalizator).

Lepiej jest wykryć tego rodzaju problemy za pomocą statycznego narzędzia do analizy kodu, które może wykryć wszystkie miejsca w kodzie, w których zapomnisz wyrzucić połączenie. Jest jeden zbudowany w in Visual Studio lub możesz użyć FxCop.

Aby pomóc Ci nie zapomnieć usuwania połączenia, to jest dobry pomysł, aby:

  • Trzymać cały kod połączenia DB w jednej warstwie/montaż/modułu, więc to nie jest rozpraszane przez projekty.
  • Mieć metody użytkowe do wykonywania poleceń SQL i zwracania wyników; więc nie tworzysz SQLConnection więcej miejsc niż potrzebujesz.
  • Pamiętaj, aby korzystać z C# using construct.
+2

+1 Najlepszym sposobem podejścia jest uzyskanie podstawowej przyczyny i usunięcie połączeń. –

1

Jeśli twoja aplikacja korzysta z SQL Connection Pooling (domyślne), nie ma to znaczenia, ponieważ połączenia są ponownie używane i nie są zamykane podczas wywoływania .Close() lub opuszczania bloku using() {} .

http://msdn.microsoft.com/en-us/library/8xx3tyca.aspx

Aby odpowiedzieć na to pytanie, ja nie wierzę, że jest to wydarzenie dla kolekcji GC, jednak to nie będzie miało znaczenia, jeśli nie było, bo nigdy nie wiadomo, czy GC wybrał do recyklingu swój przedmiot w tej kolekcji pass (nie wszystkie obiekty są czyszczone z powodu algorytmu generacyjnego).

Unikałbym również prób użycia timerów i "czeków", ponieważ prawdopodobnie będziesz mieć odniesienia do obiektu i zapobiec jego rozłączeniu.

+0

Ale ma to znaczenie, ponieważ jeśli połączenia nie są zamknięte, nie są one zwracane do puli, więc nie będą ponownie wykorzystywane natychmiast. Może to spowodować wyczerpanie puli połączeń lub pochłonięcie cennych zasobów powodujących problemy. –

+1

Zgadzam się, ale chodzi o to, że OP chce wiedzieć "kiedy" następuje wywóz śmieci. Chodzi mi o to, że kolekcja połączeń sql nie będzie występować z łączeniem. Jedynym rozwiązaniem jest faktyczne naprawienie i zapewnienie wywołania funkcji .Close(), a nie poleganie na zdarzeniu GC collect lub sprawdzeniu timera. – David

0

Po pierwsze, jeśli rozważasz nawet użycie GC, masz gdzieś poważne problemy. Sugeruję, że najlepszym sposobem zapewnienia połączeń kodowych Close jest przetestowanie kodu za pomocą takich technik, jak mocking. Jeśli twój test jednostki wskazuje, że Close został wywołany, twój kod jest poprawny i nie musisz robić niczego niepotrzebnie niebezpiecznego, jak grzebanie z Garbage Collectorem.

Po drugie, jeśli nadal nalegać na zamiar wykonania sprawdzenia trasy The tylko rzeczą, którą należy jeszcze rozważyć ten jest spinanie zdarzenie StateChange z SqlConnection. To zadziała, jeśli na przykład ConnectionState zmieni się z Open na Closed.

1

Nie jestem bardzo obeznana z śmieciarzem i nie wiem, czy istnieje bezpośredni sposób na uzyskanie informacji, ale mój pierwszy pomysł jest następujący.

Po prostu stwórz weak reference na obojętny obiekt. Jeśli spojrzysz na słabe odwołanie później, a obiekt nie będzie już dostępny, prawdopodobnie możesz założyć, że nastąpiło zbieranie śmieci.

(. Odpowiedź ta ma zastosowanie wyłącznie do wykrywania śmieciarza biegnie ja całkowicie ignorowane powód ten sposób - są znacznie lepsze strategie radzenia sobie z wyciekiem zasobów.)

+0

To wszystko, musisz się pozbyć, aby zapobiec finalizacji. Jeśli po prostu zachowujesz słaby ref i sprawdzasz, czy to się stało, jesteś już za późno. (Garbage Collection pochodzi po sfinalizowaniu). –

+0

Tak, oczywiście. Jak stwierdzono w ostatnim zdaniu, chciałem tylko odpowiedzieć "Wykrywanie, czy wywoływacz został wywołany".Nie powinieneś pisać kodu, który przecieka zasoby w pierwszej kolejności. Można tego skutecznie uniknąć, przeprowadzając analizę statyczną i profilowanie w czasie wykonywania. –

Powiązane problemy