ProgramingTip

System.Data.SQLite Close ()가 데이터베이스 파일을 해제하지 않습니다.

bestdevel 2020. 10. 4. 12:01
반응형

System.Data.SQLite Close ()가 데이터베이스 파일을 해제하지 않습니다.


파일을 삭제하기 전에 데이터베이스를 닫는 데 문제가 있습니다. 코드는

 myconnection.Close();    
 File.Delete(filename);

그리고 삭제는 파일이 아직 사용 중이라는 예외를 발생했습니다. 몇 분 후에 디버거에서 삭제 ()를 다시 시도해 타이밍 문제가 아닙니다.

Close () 호출 전에 전혀 실행되지 않습니다. 그래서 나는 그것이 공개 된 거래가 확신합니다. SQL 명령은 선택 사항입니다.

ProcMon은 데이터베이스 파일을보고있는 프로그램과 바이러스 백신을 보여줍니다. close () 나중에 db 파일을 해제하는 프로그램이 표시되지 않습니다.

Visual Studio 2010, C #, System.Data.SQLite 버전 1.0.77.0, Win7

이와 같은 2 년 된 버그를 변경하고 로그에 수정 표시됩니다.

내가 확인할 수있는 다른 서열입니까? 열려있는 명령 또는 트랜잭션 목록을 이용하는 방법이 있습니까?


새로운 작동 코드 :

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

얼마 전 C # 용 DB 추상화 계층을 작성하는 동안 동일한 문제가 발생하는 것이 문제가 무엇인지 알아. 내 라이브러리를 사용하여 SQLite DB를 삭제하려고 할 때 예외가 발생했습니다.

어쨌든, 오늘 오후에 나는 모든 것을 다시 그렇게하는 것이 왜 단번에 그렇게했는지 알아 내고 노력할 생각했습니다. 그래서 여기에 제가 지금까지 그것이 있습니다.

호출 하면 SQLite 데이터베이스 인스턴스를 여러 SQLiteConnection.Close()검사 및 기타 사항과 함께 SQLiteConnectionHandle처리합니다. 이 작업은에 대한 호출을 통해 SQLiteConnectionHandle.Dispose()수행 할 때까지 CLR의 가비지 수집기가 일부 가비지 수집을 수행 할 때까지 호출을 해제하지 않습니다. (다른 함수를 통해) 호출 SQLiteConnectionHandleCriticalHandle.ReleaseHandle()함수를 재정의 언어 sqlite3_close_interop()데이터베이스를 닫지.

내 관점에서 프로그래머가 데이터베이스가 닫힐 때 실제로 설치하지 않기 때문에 일을 수행하는 매우 나쁜 방법이지만 그것이 수행되는 방식으로 지금은 함께 살아야하거나 수행해야합니다. System.Data.SQLite에 대한 몇 가지 변경 사항. 모든 자원 봉사자들은 그렇게 할 수 있습니다. 불행히도 내년 이전에 그렇게 할 시간이 없습니다.

TL; DR 솔루션 은를 한 후 호출 SQLiteConnection.Close()호출하기 전에 GC 실행하는 강제를 을 구석으로 File.Delete()입니다.

다음은 샘플 코드입니다.

string filename = "testFile.db";
SQLiteConnection connection = new SQLiteConnection("Data Source=" + filename + ";Version=3;");
connection.Close();
GC.Collect();
GC.WaitForPendingFinalizers();
File.Delete(filename);

행운을 빕니다. 도움이되기를 바랍니다.


그냥 GC.Collect()나를 위해 작동하지 않습니다.

파일 삭제를 진행하기 위해 GC.WaitForPendingFinalizers()나중에 추가해야했습니다 GC.Collect().


경우에는 제 SQLiteCommand명시 적으로 삭제하지 않고 object-를 생성 했습니다.

var command = connection.CreateCommand();
command.CommandText = commandText;
value = command.ExecuteScalar();

using명령문에 내 명령을 래핑하고 문제를 해결했습니다.

static public class SqliteExtensions
{
    public static object ExecuteScalar(this SQLiteConnection connection, string commandText)
    {
        using (var command = connection.CreateCommand())
        {
            command.CommandText = commandText;
            return command.ExecuteScalar();
        }
    }
}

using문은 예외가 발생하더라도 Dispose가 호출됩니다.

그리고 명령을 실행하는 것도 있고.

value = connection.ExecuteScalar(commandText)
// Command object created and disposed

다음은 나를 위해 일했습니다.

MySQLiteConnection.Close();
SQLite.SQLiteConnection.ClearAllPools()

추가 정보 : 연결은 성능 향상을 위해 SQLite에 의해 풀링됩니다. 즉, 연결 개체에서 Close 메서드를 호출하면 데이터베이스에 대한 연결이 여전히 활성화되어 (백그라운드에서) 다음 Open 메서드가 더 빨라질 수 있습니다. 더 이상 새 연결을 사용하지 않는 파일 경우 ClearAllPools를 호출하면 백그라운드에서 모든 연결이 닫히고 db 파일 핸들이 해제됩니다. 그런 다음 db 파일이 제거되거나 삭제되거나 다른 프로세스에서 수 있습니다.


해결할 문제가 있었지만 가비지 수집기 솔루션으로 해결되지 않았습니다.

사용 후 폐기 SQLiteCommandSQLiteDataReader개체를 발견 하면 가비지 수집기를 사용하여 전혀 구할 수 없습니다.

SQLiteCommand command = new SQLiteCommand(sql, db);
command.ExecuteNonQuery();
command.Dispose();

많은 문제가 발생하여 파일을 시도했던 GC.Collect적이 있었는데 오랜 시간이 있습니다.

SQLiteCommandTableAdapters에서 기본 s 처리와 관련된 대체 솔루션을 찾았습니다 . 자세한 내용 이 답변참조하십시오 .


모든 시도하십시오 ...이 위의 코드를 시도합니다 ... 나를 위해 일했습니다.

    Reader.Close()
    connection.Close()
    GC.Collect()
    GC.WaitForPendingFinalizers()
    command.Dispose()
    SQLite.SQLiteConnection.ClearAllPools()

도움이되는 희망


사용하다 GC.WaitForPendingFinalizers()

예 :

Con.Close();  
GC.Collect();`
GC.WaitForPendingFinalizers();
File.Delete(Environment.CurrentDirectory + "\\DATABASENAME.DB");

신고 문제가 있습니다. 가비지 수집기를 호출해도 도움이되지 않습니다. 나중에 문제를 방법을 찾았습니다.

작성자는 또한 데이터베이스를 삭제하기 전에 해당 데이터베이스에 SELECT 쿼리를 수행하고 사용했습니다. 저도 같은 상황입니다.

다음 코드가 있습니다.

SQLiteConnection bc;
string sql;
var cmd = new SQLiteCommand(sql, bc);
SQLiteDataReader reader = cmd.ExecuteReader();
reader.Read();
reader.Close(); // when I added that string, the problem became solved.

또한 연결 데이터베이스를 닫고 가비지 수집기를 호출 할 필요가 없습니다. 내가해야 할 일은 생성되는 SELECT 쿼리를 실행하는 동안 생성 된 리더를 닫는 것입니다.


GC는 전혀 다룰 필요가 없습니다. 모든 것이 sqlite3_prepare완료했는지 확인하십시오 .

각각 sqlite3_prepare에 대해 특파원이 필요합니다 sqlite3_finalize.

문서 마무리하지 문서 sqlite3_close연결이 닫히지 언어로 연결됩니다.


나는 EF와 System.Data.Sqlite.

나를 위해 파일 잠금이 발생하는 빈도를 발견 SQLiteConnection.ClearAllPools()하고 GC.Collect()수 있었지만 여전히 가끔 발생합니다 (약 1 % 시간).

나는 조사를 해왔고 SQLiteCommandEF가 생성 하는 일부 는 폐기되지 않았고 여전히 Connection 속성이 닫힌 연결로 설정된 것 같습니다. 나는 이것을 폐기하려고 시도했지만 Entity Framework는 다음 DbContext읽기 중에 예외를 throw합니다. EF는 연결이 닫힌 후에도 여전히 사용하는 것 같습니다.

내 솔루션은 Null연결이 이러한 SQLiteCommands에서 닫힐 때 Connection 속성이로 설정되어 있는지 확인하는 것 입니다. 이것은 파일 잠금을 해제하기에 충분한 것 같습니다. 아래 코드를 테스트했지만 수천 번의 테스트 후에도 파일 잠금 문제를 보지 못했습니다.

public static class ClearSQLiteCommandConnectionHelper
{
    private static readonly List<SQLiteCommand> OpenCommands = new List<SQLiteCommand>();

    public static void Initialise()
    {
        SQLiteConnection.Changed += SqLiteConnectionOnChanged;
    }

    private static void SqLiteConnectionOnChanged(object sender, ConnectionEventArgs connectionEventArgs)
    {
        if (connectionEventArgs.EventType == SQLiteConnectionEventType.NewCommand && connectionEventArgs.Command is SQLiteCommand)
        {
            OpenCommands.Add((SQLiteCommand)connectionEventArgs.Command);
        }
        else if (connectionEventArgs.EventType == SQLiteConnectionEventType.DisposingCommand && connectionEventArgs.Command is SQLiteCommand)
        {
            OpenCommands.Remove((SQLiteCommand)connectionEventArgs.Command);
        }

        if (connectionEventArgs.EventType == SQLiteConnectionEventType.Closed)
        {
            var commands = OpenCommands.ToList();
            foreach (var cmd in commands)
            {
                if (cmd.Connection == null)
                {
                    OpenCommands.Remove(cmd);
                }
                else if (cmd.Connection.State == ConnectionState.Closed)
                {
                    cmd.Connection = null;
                    OpenCommands.Remove(cmd);
                }
            }
        }
    }
}

사용하려면 ClearSQLiteCommandConnectionHelper.Initialise();응용 프로그램로드 시작시 호출 하십시오. 그러면 활성 명령 목록이 유지되고 Null닫힌 연결을 가리킬 때 연결이로 설정됩니다 .


나는 그 요청 SQLite.SQLiteConnection.ClearAllPools()이 가장 깨끗한 해결책 이라고 믿습니다 . 내가 아는 GC.Collect()한 WPF 환경에서 수동으로 호출하는 것은 적절하지 않습니다 . 하지만 System.Data.SQLite2016 년 3 월에 1.0.99.0으로 업그레이드 할 때까지 문제를 인지하지 못했습니다.


나는 비슷한 문제로 어려움을 겪고 있었다. 부끄러워 ... 마침내 Reader 가 닫히지 않았다는 것을 깨달았 습니다. 어떤 이유로 해당 연결이 닫히면 리더가 닫힐 것이라고 생각했습니다. 분명히 GC.Collect ()는 나를 위해 작동하지 않았습니다.
Reader를 "using : 문으로 감싸는 것도 좋은 생각입니다. 여기에 간단한 테스트 코드가 있습니다.

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

EF6에서 SQLite 1.0.101.0을 사용하고 있었고 모든 연결과 엔티티가 삭제 된 후 파일이 잠기는 문제가 발생했습니다.

이것은 EF의 업데이트가 완료된 후 데이터베이스를 잠근 상태로 유지하면서 악화되었습니다. GC.Collect ()는 도움이 된 유일한 해결 방법이었고 절망하기 시작했습니다.

필사적으로 Oliver Wickenden의 ClearSQLiteCommandConnectionHelper를 사용해 보았습니다 (7 월 8 일 그의 답변 참조). 환상적입니다. 모든 잠금 문제가 사라졌습니다! 감사합니다 올리버.


가비지 콜렉터를 기다리는 동안 데이터베이스가 항상 해제되지 않을 수 있습니다. 예를 들어 PrimaryKey에 대한 기존 값이있는 행을 삽입하려고 시도하는 경우와 같이 SQLite 데이터베이스에서 일부 유형의 예외가 발생하면 사용자가 처리 할 때까지 데이터베이스 파일이 보관됩니다. 다음 코드는 SQLite 예외를 포착하고 문제가있는 명령을 취소합니다.

SQLiteCommand insertCommand = connection.CreateCommand();
try {
    // some insert parameters
    insertCommand.ExecuteNonQuery();
} catch (SQLiteException exception) {
    insertCommand.Cancel();
    insertCommand.Dispose();
}

문제가있는 명령의 예외를 처리하지 않으면 Garbage Collector가 처리 할 수없는 예외가 있습니다. 이러한 명령에 대해 처리되지 않은 예외가 있으므로 가비지가 아닙니다. 이 처리 방법은 가비지 수집기를 기다리는 데 잘 작동했습니다.


이것은 나를 위해 작동하지만 때로는 프로세스가 닫힐 때 저널 파일 -wal -shm이 삭제되지 않는 것으로 나타났습니다. 모든 연결이 닫 혔을 때 SQLite가 -wal -shm 파일을 제거하도록하려면 마지막 연결이 읽기 전용이 아니어야합니다. 이것이 누군가를 도울 수 있기를 바랍니다.

참고 URL : https://stackoverflow.com/questions/8511901/system-data-sqlite-close-not-releasing-database-file

반응형