2012-09-21 97 views
30

我在ASP.NET應用程序(框架4.0)中使用System.Data.SQLite提供程序。 我遇到的問題是,當我在SQLite數據庫中的某個表中插入某些內容時,數據庫被鎖定,並且即使在處理連接後鎖也不會被釋放。SQLite保持數據庫鎖定,即使連接關閉後

當試圖訪問文件時,錯誤是:「進程無法訪問文件'catalog.sqlite',因爲它正在被另一個進程使用。」我打開連接,從SQLServer數據庫中讀取一些數據,將數據插入到SQLite(通過SQLiteDataAdapter),然後關閉連接並處理所有的事情,只是爲了安全起見。但是,當我在文件填充數據後嘗試壓縮文件時,我仍然遇到了該錯誤。

我已經閱讀了StackOverflow的所有建議,但沒有一個幫助解決問題(關閉防病毒,更改事務模型,等待幾秒鐘,然後再壓縮文件,包裝所有插入調用進入交易等等,但沒有任何幫助解決這個問題

也許有什麼特定於ASP.NET(多線程是問題?即使我在一個開發機器上測試它只有一個調用該函數和沒有併發?)

作爲一個方面說明,我試圖避免DataTable和SQLiteDataAdapter和只使用SQLiteCommand直接和這種方式它工作一個ch臂。當然,我可以繼續構建我的查詢作爲字符串,而不是使用數據適配器,但是當有框架構建時,我覺得有點尷尬。

+2

你是否在使用語句中包裝命令和連接? – Arran

+0

您的應用程序是否鎖定該鎖定,即鎖定退出時是否消失? –

+0

@Arran我不打包使用連接。但即使使用沒有鎖定的版本(即使用命令而不是數據適配器),我也不打包它。 –

回答

31

我使用與System.Data.Sqlite.dll版本1.0.82.0一起提供的設計器生成的數據集/表適配器有同樣的問題 - 關閉連接後,我們無法使用System.IO.FileStream讀取數據庫文件。我正確處理連接和tableadapters,我沒有使用連接池。

根據我的第一次搜索(例如thisthis thread),這似乎是圖書館本身存在的一個問題 - 無論是對象未正確釋放還是共用問題(我不使用)。

閱讀完您的問題後,我嘗試僅使用SQLiteCommand對象複製問題,並且發現問題出現在您不處理它們時。 更新2012-11-27 19:37 UTC:System.Data.SQLite的this ticket進一步證實了這一點,其中開發人員解釋說「全部與連接關聯的SQLiteCommand和SQLiteDataReader對象[應該]正確處置」。

然後我又回到生成的TableAdapters上,我看到沒有實現Dispose方法 - 所以實際上創建的命令沒有被處置。我實現了它,負責處理所有的命令,而且我沒有問題。

以下是C#中的代碼,希望對您有所幫助。請注意,代碼是從original in Visual Basic轉換而來的,因此需要一些轉換錯誤。

//In Table Adapter  
protected override void Dispose(bool disposing) 
{ 
    base.Dispose(disposing); 

    Common.DisposeTableAdapter(disposing, _adapter, _commandCollection); 
} 

public static class Common 
{ 
    /// <summary> 
    /// Disposes a TableAdapter generated by SQLite Designer 
    /// </summary> 
    /// <param name="disposing"></param> 
    /// <param name="adapter"></param> 
    /// <param name="commandCollection"></param> 
    /// <remarks>You must dispose all the command, 
    /// otherwise the file remains locked and cannot be accessed 
    /// (for example, for reading or deletion)</remarks> 
    public static void DisposeTableAdapter(
     bool disposing, 
     System.Data.SQLite.SQLiteDataAdapter adapter, 
     IEnumerable<System.Data.SQLite.SQLiteCommand> commandCollection) 
    { 
     if (disposing) { 
      DisposeSQLiteTableAdapter(adapter); 

      foreach (object currentCommand_loopVariable in commandCollection) 
      { 
       currentCommand = currentCommand_loopVariable; 
       currentCommand.Dispose(); 
      } 
     } 
    } 

    public static void DisposeSQLiteTableAdapter(
      System.Data.SQLite.SQLiteDataAdapter adapter) 
    { 
     if (adapter != null) { 
      DisposeSQLiteTableAdapterCommands(adapter); 

      adapter.Dispose(); 
     } 
    } 

    public static void DisposeSQLiteTableAdapterCommands(
      System.Data.SQLite.SQLiteDataAdapter adapter) 
    { 
     foreach (object currentCommand_loopVariable in { 
      adapter.UpdateCommand, 
      adapter.InsertCommand, 
      adapter.DeleteCommand, 
      adapter.SelectCommand}) 
     { 
      currentCommand = currentCommand_loopVariable; 
      if (currentCommand != null) { 
       currentCommand.Dispose(); 
      } 
     } 
    } 
} 

更新2013年7月5日17:36 UTCgorogm's answer突出了兩個重要的事情:

  • 根據changelog上System.Data.SQLite的官方網站,從版本開始1.0.84.0上面的代碼不應該被需要,因爲庫處理這個。我沒有測試過這一點,但在最壞的情況下,你只需要這個片段:

    //In Table Adapter  
    protected override void Dispose(bool disposing) 
    { 
        base.Dispose(disposing); 
    
        this.Adapter.Dispose(); 
    } 
    
  • 有關Dispose呼叫TableAdapter的執行:它是最好把這個分部類,所以數據集重新生成不會影響此代碼(以及您可能需要添加的任何其他代碼)。

+0

我看到使用Entity框架和最新的1.82.0 sqlite的非常大的內存泄漏。你認爲這是問題嗎? – Peter

+0

也許,因爲(我認爲,我在EF中缺乏經驗),可能會有與'SQLiteCommand'等待發布有關的非託管資源。 [This SO線程](http://stackoverflow.com/questions/8511901/system-data-sqlite-close-not-releasing-database-file)和[這張票](http://system.data.sqlite。組織/ index.html的/ tktview?name = ab8b0ccbf6)似乎證實了你的假設。爲避免泄漏,您可以嘗試在EF中禁用「多個活動結果集」,或者嘗試使用託管庫,如[C#-SQLite](http://code.google.com/p/csharp-sqlite/) 。希望這可以幫助。 – edymtt

+0

這是解決我的問題,謝謝 –

22

我有同樣的問題。我的場景是在獲取SQLite數據庫文件中的數據之後,我想刪除該文件,但它總是拋出一個錯誤「...正在使用其他進程」。即使我處置SqliteConnection或SqliteCommand錯誤仍然發生。我通過調用GC.Collect()修復了錯誤。

代碼片斷

public void DisposeSQLite() 
{ 
    SQLiteConnection.Dispose(); 
    SQLiteCommand.Dispose(); 

    GC.Collect(); 
} 

希望這有助於。

+0

我也有同樣的問題,我不得不使用 GC.Collect(); 參考: http://system.data.sqlite.org/index.html/tktview/6434e23a0f63b440?plaintext 希望下一個Sqlite版本將解決此問題。 – peterincumbria

+0

myyyyyyyyyy god !!!!謝謝!!!真!!!我使用了幾天來找到答案!但沒有人來與GC.Collect();哪些工作。 omfg ...謝謝!!! – Assassinbeast

+2

@klaydze @Assassinbeast處理您提到的問題的更清晰的方法是調用'System.Data.SQLite.SQLiteConnection.ClearAllPools();'而不是兩次處理調用+ GC.Collect –

8

在我的情況下,我創建的SQLiteCommand對象沒有明確處置它們。

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

我用using聲明包裝我的命令,它解決了我的問題。

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

然後你可以使用它像這樣

connection.ExecuteScalar(commandText); 
+2

我強烈建議不要吞嚥這樣的異常! –

+1

同一人,同一評論,不同的答案。修正了,謝謝。 – Nate

+0

這究竟是如何吞噬異常?當我使用這個時,如果命令失敗,我會在using語句的末尾拋出異常。 – Miebster

1

我發現edymtt的回答正確關於責備數據集但TableAdapter的/,而不是修改每一次重新生成的TableAdapter的CodeFile,我發現了一個其他的解決辦法:手動調用.Dispose在TableAdapter的子元素上。 (在.NET 4.5,最新的SQLite 1.0.86)

using (var db = new testDataSet()) 
{ 
    using (testDataSetTableAdapters.UsersTableAdapter t = new testDataSetTableAdapters.UsersTableAdapter()) 
    { 
     t.Fill(db.Users); 
     //One of the following two is enough 
     t.Connection.Dispose(); //THIS OR 
     t.Adapter.Dispose(); //THIS LINE MAKES THE DB FREE 
    } 
    Console.WriteLine((from x in db.Users select x.Username).Count()); 
} 
+0

當我更改設計器中的數據集時,我不必修改'TableAdapters' - 我已經使用[部分類]實現了'Dispose'(http://msdn.microsoft.com/zh-cn/library/wa80x488 %28V = vs.110%29.aspx)。感謝您指出在最新版本的System.Data.SQLite中,我的代碼不再是必需的(請參閱[changelog](http://system.data.sqlite.org/index.html/doc/trunk/www/news .wiki)for release 1.0.84。 – edymtt

1

在大多數情況下,如果你不處理你的讀者和正確命令會出現問題。有一種情況是命令和讀者不能正確處置。

情況1:如果您正在運行布爾型函數。在達到結果之前,finally塊中的代碼不會超出。如果您要在執行代碼時評估函數isDataExists的結果(如果它適合結果i),那麼這是個大問題。Ë

if(isDataExists){ 
     // execute some code 
    } 

功能正在評估

public bool isDataExists(string sql) 
    { 
     try 
     { 
      OpenConnection(); 
      SQLiteCommand cmd = new SQLiteCommand(sql, connection); 
      reader = cmd.ExecuteReader(); 
      if (reader != null && reader.Read()) 
      { 
       return true; 
      } 
      else 
      { 
       return false; 
      } 
     } 
     catch (Exception expMsg) 
     { 
      //Exception 
     } 
     finally 
     { 
      if (reader != null) 
      { 
       reader.Dispose(); 
      } 
      CloseConnection(); 
     } 
     return true; 
    } 

解決方案:處置try塊內你的讀者和命令如下

  OpenConnection(); 
      SQLiteCommand cmd = new SQLiteCommand(sql, connection); 
      reader = cmd.ExecuteReader(); 
      if (reader != null && reader.Read()) 
      { 
       cmd.Dispose(); 
       CloseConnection(); 
       return true; 
      } 
      else 
      { 
       cmd.Dispose(); 
       CloseConnection(); 
       return false; 
      } 

最後處置閱讀器和命令剛萬一有些事情出錯了

 finally 
     { 
      if (reader != null) 
      { 
       reader.Dispose(); 
      } 
      CloseConnection(); 
     } 
+0

如果你把命令放在'try'塊中,在ExecuteReader()期間發生的異常將導致該命令不被處理,你應該使用'using'塊,或者如果你更喜歡自己寫出來,你可以嵌套多個'try' /'finally'塊。 –

0

這是我在碰到這個錯誤時找到的頂級谷歌搜索結果之一。然而,沒有任何回覆幫助我,所以經過更多的搜索和谷歌搜索後,我想出了這個代碼,從http://www.tsjensen.com/blog/post/2012/11/10/SQLite-on-Visual-Studio-with-NuGet-and-Easy-Instructions.aspx

一些代碼的作品。但是,我根本不必使用NuGet。我的程序所做的是每次打開服務器時從服務器下載一個db文件。然後,如果用戶更新該數據庫,則會爲每個人上傳,以便他們下次打開相同的程序。在更新本地文件並嘗試將其上傳到我們的SharePoint後,我收到文件正在使用的錯誤。現在它工作正常。

Public Function sqLiteGetDataTable(sql As String) As DataTable 
    Dim dt As New DataTable() 
    Using cnn = New SQLiteConnection(dbConnection) 
     cnn.Open() 
     Using cmd As SQLiteCommand = cnn.CreateCommand() 
      cmd.CommandText = sql 
      Using reader As System.Data.SQLite.SQLiteDataReader = cmd.ExecuteReader() 
       dt.Load(reader) 
       reader.Dispose() 
      End Using 
      cmd.Dispose() 
     End Using 
     If cnn.State <> System.Data.ConnectionState.Closed Then 
      cnn.Close() 
     End If 
     cnn.Dispose() 
    End Using 
    Return dt 
End Function 
5

以下爲我工作: MySQLiteConnection.Close(); SQLite.SQLiteConnection.ClearAllPools()

0

確保任何IDisposable的(例如,SQLiteConnection,SQLiteCommand等)被適當地設置解決了這個問題。我應該重申,必須使用「使用」作爲習慣來確保適當處置可支配資源。

1

如前所述,必須銷燬SQLite對象。但是,有一個奇怪的行爲:連接必須在調用期間打開在命令上處理。例如:

using(var connection = new SqliteConnection("source.db")) 
{ 
    connection.Open(); 
    using(var command = connection.CreateCommand("select...")) 
    { 
     command.Execute... 
    } 
} 

工作正常,但:

using(var connection = new SqliteConnection("source.db")) 
{ 
    connection.Open(); 
    using(var command = connection.CreateCommand("select...")) 
    { 
     command.Execute... 
     connection.Close(); 
    } 
} 

給出了同樣的文件鎖定

0

我有同樣的問題,它只是通過設置在using聲明DbCommand固定,但與Pooling = true我的問題已修復!

   SQLiteConnectionStringBuilder builder = new SQLiteConnectionStringBuilder 
       { 
        Pooling = true 
       };