Mam problem z zamknięciem bazy danych przed próbą usunięcia pliku. Kod jest po prostu
myconnection.Close();
File.Delete(filename);
A Delete zgłasza wyjątek, że plik jest nadal używany. Po kilku minutach ponownie wypróbowałem funkcję Delete () w debugerze, więc nie jest to problem z synchronizacją.
Mam kod transakcji, ale w ogóle nie działa przed wywołaniem Close (). Więc jestem prawie pewien, że nie jest to otwarta transakcja. Polecenia sql między otwieraniem a zamykaniem to tylko zaznaczenia.
ProcMon pokazuje mój program i mój program antywirusowy przeglądający plik bazy danych. Nie pokazuje, że mój program zwalnia plik db po wykonaniu funkcji close ().
Visual Studio 2010, C #, System.Data.SQLite w wersji 1.0.77.0, Win7
Widziałem taki błąd sprzed dwóch lat, ale dziennik zmian mówi, że został naprawiony.
Czy jest coś jeszcze, co mogę sprawdzić? Czy jest sposób, aby uzyskać listę otwartych poleceń lub transakcji?
Nowy, działający kod:
db.Close();
GC.Collect(); // yes, really release the db
bool worked = false;
int tries = 1;
while ((tries < 4) && (!worked))
{
try
{
Thread.Sleep(tries * 100);
File.Delete(filename);
worked = true;
}
catch (IOException e) // delete only throws this on locking
{
tries++;
}
}
if (!worked)
throw new IOException("Unable to close file" + filename);
źródło
SQLiteAsyncConnection.ResetPool()
, zobacz ten problem, aby uzyskać szczegółowe informacje.Odpowiedzi:
Napotkałem ten sam problem jakiś czas temu podczas pisania warstwy abstrakcji DB dla C # i nigdy nie udało mi się znaleźć przyczyny problemu. Skończyło się na tym, że rzuciłem wyjątek, gdy próbowałeś usunąć bazę danych SQLite za pomocą mojej biblioteki.
W każdym razie tego popołudnia przeglądałem to wszystko jeszcze raz i pomyślałem, że spróbuję dowiedzieć się, dlaczego robi to raz na zawsze, więc oto, co znalazłem do tej pory.
To, co się dzieje, kiedy wywołujesz,
SQLiteConnection.Close()
jest takie, że (wraz z wieloma sprawdzeniami i innymi rzeczami),SQLiteConnectionHandle
co wskazuje na instancję bazy danych SQLite, zostaje usunięte. Odbywa się to poprzez wywołanie funkcjiSQLiteConnectionHandle.Dispose()
, jednak w rzeczywistości nie zwalnia to wskaźnika, dopóki moduł zbierający elementy bezużyteczne środowiska CLR nie wykona pewnych operacji czyszczenia pamięci. PonieważSQLiteConnectionHandle
przesłaniaCriticalHandle.ReleaseHandle()
funkcję do wywołaniasqlite3_close_interop()
(za pośrednictwem innej funkcji), nie zamyka to bazy danych.Z mojego punktu widzenia jest to bardzo zły sposób na robienie rzeczy, ponieważ programista nie jest tak naprawdę pewien, kiedy baza danych zostanie zamknięta, ale tak to zostało zrobione, więc myślę, że musimy na razie z tym żyć lub zatwierdzić kilka zmian w System.Data.SQLite. Zapraszamy do tego wszystkich wolontariuszy, niestety nie mam na to czasu przed przyszłym rokiem.
TL; DR Rozwiązaniem jest wymuszenie GC po wywołaniu
SQLiteConnection.Close()
i przed wywołaniemFile.Delete()
.Oto przykładowy kod:
Powodzenia i mam nadzieję, że to pomoże
źródło
Po prostu
GC.Collect()
nie działało dla mnie.Musiałem dodać
GC.WaitForPendingFinalizers()
poGC.Collect()
, aby kontynuować usuwanie pliku.źródło
GC.Collect()
po prostu uruchamia usuwanie śmieci, które jest asynchroniczne, więc aby upewnić się, że wszystko zostało wyczyszczone, musisz wyraźnie poczekać na to.W moim przypadku tworzyłem
SQLiteCommand
obiekty bez ich jawnego usuwania.Zawinąłem polecenie w
using
oświadczeniu i rozwiązało to mój problem.Wtedy też dużo łatwiej jest wykonywać polecenia.
źródło
Miałem podobny problem, chociaż rozwiązanie odśmiecania go nie rozwiązało.
Znalezione utylizacja
SQLiteCommand
iSQLiteDataReader
przedmioty po użyciu w ogóle uratowały mnie przy użyciu garbage collectora.źródło
SQLiteCommand
nawet jeśliSQLiteCommand
później ponownie wykorzystasz zmienną.command.Dispose();
do każdegoSQLiteCommand
, co zostało wykonane..Dispose()
zwalniasz (tj. ) Inne obiekty, takie jak SQLiteTransaction, jeśli takie masz.Pracowały dla mnie:
Więcej informacji : Połączenia są gromadzone w puli przez SQLite w celu zwiększenia wydajności, co oznacza, że gdy wywołasz metodę Close na obiekcie połączenia, połączenie z bazą danych może nadal być aktywne (w tle), dzięki czemu następna metoda Open stanie się szybsza. nie chcesz już nowego połączenia, wywołanie ClearAllPools zamyka wszystkie połączenia, które są aktywne w tle, a uchwyty do pliku db zostają zwolnione. Następnie plik db może zostać usunięty, skasowany lub użyty przez inny proces.
źródło
SQLiteConnectionPool.Shared.Reset()
. Spowoduje to zamknięcie wszystkich otwartych połączeń. W szczególności jest to rozwiązanie, jeśli używasz,SQLiteAsyncConnection
które nie maClose()
metody.Miałem podobny problem, próbowałem go rozwiązać
GC.Collect
ale jak wspomniano, może minąć dużo czasu, zanim plik nie zostanie zablokowany.Znalazłem alternatywne rozwiązanie, które obejmuje pozbycie się bazowych
SQLiteCommand
s w TableAdapters, zobacz tę odpowiedź, aby uzyskać dodatkowe informacje.źródło
Spróbuj tego ... ten próbuje wszystkich powyższych kodów ... zadziałał dla mnie
Mam nadzieję, że to pomoże
źródło
Miałem ten sam problem z EF i
System.Data.Sqlite
.Dla mnie znalazłem
SQLiteConnection.ClearAllPools()
iGC.Collect()
zmniejszyłem częstotliwość blokowania pliku, ale czasami zdarzało się to (około 1% czasu).Badałem i wydaje się, że niektóre
SQLiteCommand
pliki, które tworzy EF, nie są usuwane i nadal mają właściwość Connection ustawioną na zamknięte połączenie. Próbowałem je usunąć, ale Entity Framework zgłosiłby wyjątek podczas następnegoDbContext
odczytu - wydaje się, że EF czasami nadal używa ich po zamknięciu połączenia.Moim rozwiązaniem było upewnienie się, że właściwość Connection jest ustawiona na,
Null
gdy połączenie zostanie zamknięte na tychSQLiteCommand
dyskach. Wydaje się, że to wystarczy, aby zwolnić blokadę pliku. Testowałem poniższy kod i po kilku tysiącach testów nie widziałem żadnych problemów z blokowaniem plików:Aby użyć wystarczy zadzwonić
ClearSQLiteCommandConnectionHelper.Initialise();
na początku ładowania aplikacji. Spowoduje to zachowanie listy aktywnych poleceń i ustawi ich połączenie,Null
gdy wskażą połączenie, które jest zamknięte.źródło
Posługiwać się
GC.WaitForPendingFinalizers()
Przykład:
źródło
Miałem podobny problem. Dzwonienie do Garbage Collectora mi nie pomogło. LAter znalazłem sposób na rozwiązanie problemu
Autor napisał również, że wykonał zapytania SELECT do tej bazy danych przed próbą jej usunięcia. Mam taką samą sytuację.
Mam następujący kod:
Nie muszę też zamykać połączenia z bazą danych i dzwonić do Garbage Collectora. Wystarczyło zamknąć czytnik, który powstał podczas wykonywania zapytania SELECT
źródło
Uważam, że wezwanie
SQLite.SQLiteConnection.ClearAllPools()
jest najczystszym rozwiązaniem. O ile wiem, ręczne wywoływanieGC.Collect()
w środowisku WPF nie jest właściwe . Chociaż nie zauważyłem problemu, dopóki nie zaktualizowałem doSystem.Data.SQLite
1.0.99.0 w 3/2016źródło
Może wcale nie musisz mieć do czynienia z GC. Proszę sprawdzić, czy wszystko
sqlite3_prepare
jest sfinalizowane.Do każdego
sqlite3_prepare
potrzebujesz korespondentasqlite3_finalize
.Jeśli nie zakończysz poprawnie,
sqlite3_close
nie zamyka połączenia.źródło
Zmagałem się z podobnym problemem. Wstyd mi ... Wreszcie zdałem sobie sprawę, że Reader nie jest zamknięty. Z jakiegoś powodu myślałem, że Czytnik zostanie zamknięty po zamknięciu odpowiedniego połączenia. Oczywiście GC.Collect () nie działał dla mnie.
Dobrym pomysłem jest również umieszczenie w Czytniku instrukcji using: Oto kod szybkiego testu.
static void Main(string[] args) { try { var dbPath = "myTestDb.db"; ExecuteTestCommand(dbPath); File.Delete(dbPath); Console.WriteLine("DB removed"); } catch (Exception e) { Console.WriteLine(e.Message); } Console.Read(); } private static void ExecuteTestCommand(string dbPath) { using (var connection = new SQLiteConnection("Data Source=" + dbPath + ";")) { using (var command = connection.CreateCommand()) { command.CommandText = "PRAGMA integrity_check"; connection.Open(); var reader = command.ExecuteReader(); if (reader.Read()) Console.WriteLine(reader.GetString(0)); //without next line database file will remain locked reader.Close(); } } }
źródło
Używałem SQLite 1.0.101.0 z EF6 i miałem problem z zablokowaniem pliku po usunięciu wszystkich połączeń i jednostek.
Sytuacja pogorszyła się wraz z aktualizacjami z EF, które blokowały bazę danych po ich zakończeniu. GC.Collect () było jedynym obejściem, które pomogło i zacząłem rozpaczać.
W desperacji wypróbowałem ClearSQLiteCommandConnectionHelper Olivera Wickendena (patrz jego odpowiedź z 8 lipca). Fantastyczny. Wszystkie problemy z blokowaniem zniknęły! Dzięki Oliver.
źródło
Czekanie na Garbage Collector może nie wypuszczać bazy danych przez cały czas i to mi się przydarzyło. Kiedy jakiś rodzaj wyjątku wystąpi w bazie danych SQLite, na przykład próbując wstawić wiersz z istniejącą wartością dla PrimaryKey, plik bazy danych będzie przechowywany do momentu jego usunięcia. Poniższy kod wychwytuje wyjątek SQLite i anuluje problematyczne polecenie.
SQLiteCommand insertCommand = connection.CreateCommand(); try { // some insert parameters insertCommand.ExecuteNonQuery(); } catch (SQLiteException exception) { insertCommand.Cancel(); insertCommand.Dispose(); }
Jeśli nie poradzisz sobie z wyjątkami dla problematycznych poleceń, Garbage Collector nie może nic z nimi zrobić, ponieważ istnieją nieobsłużone wyjątki dotyczące tych poleceń, więc nie są śmieciami. Ta metoda obsługi działała dobrze w przypadku czekania na śmieciarkę.
źródło
U mnie to działa, ale zauważyłem, że czasami pliki dziennika -wal -shm nie są usuwane po zamknięciu procesu. Jeśli chcesz, aby SQLite usuwał pliki -wal -shm, gdy wszystkie połączenia są zamknięte, ostatnie zamknięte połączenie MUSI BYĆ nie tylko do odczytu. Mam nadzieję, że to komuś pomoże.
źródło
Najlepsza odpowiedź, która mi pomogła.
źródło