2012-09-21 115 views
30

ASP.NETアプリケーション(フレームワーク4.0)でSystem.Data.SQLiteプロバイダを使用しています。 私が実行している問題は、SQLiteデータベースのテーブルに何かを挿入すると、データベースがロックされ、接続が破棄されてもロックが解除されないということです。SQLiteは、接続が閉じられた後でもデータベースをロックし続けます。

ファイルにアクセスしようとすると、エラーは次のようになります。「別のプロセスで使用されているため、 'catalog.sqlite'ファイルにアクセスできません」

私のコードはかなり簡単です。私は接続を開き、SQLServerデータベースからデータを読み込み、そのデータを(SQLiteDataAdapterを介して)SQLiteに挿入し、接続を閉じてすべてを安全面に配置します。しかし、ファイルがデータで埋められた後にファイルを圧縮しようとすると、エラーが発生します。

私はここでStackOverflowに関するすべての提案を読んだことがありますが、問題の解決に役立っているものはありません(ウイルス対策、トランザクションモデルの変更、数秒待ってからファイルを圧縮し、トランザクションなどには何も含まれていませんが、この問題を解決するのに役立っていないものはありません。

多分、ASP.NETに特有のことがありますか?(マルチスレッドは問題になりますか?その機能と同時性はありませんか?)

私はDataTableとSQLiteDataAdapterを避け、SQLiteCommandを直接使用し、そのようにch腕。もちろん、データアダプタを使用する代わりにクエリを文字列として構築することもできますが、そのためにフレームワークが構築されていると、ちょっと厄介なことに気づくことがあります。

+2

ステートメントを使用してコマンドと接続をラッピングしていますか? – Arran

+0

このロックはアプリケーションによって行われていますか?つまり、終了時にロックが消えますか? –

+0

@Arran私は使用して接続をラップしていません。しかし、私はロックなしで動作するバージョン(別名、データアダプタの代わりにコマンドを使用するバージョン)でもラップしているわけではありません。 –

答えて

31

System.Data.Sqlite.dllバージョン1.0.82.0が付属しているデザイナで生成されたデータセット/テーブルを使用して同じ問題が発生しました。接続を閉じた後、System.IO.FileStreamを使用してデータベースファイルを読み取れませんでした。私は接続とテーブルの両方を正しく処分していましたが、接続プールを使用していませんでした。

ライブラリ自体に問題があると思われる私の最初の検索(たとえば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ハイライト、二つの重要な事柄:

  • System.Data.SQLiteの公式サイト上でchangelogによると、バージョンから始まります1.0.84.0上記のコードは、ライブラリがこれを処理するので、必要ではありません。私はこれをテストしていませんが、最悪の場合にはあなただけのこのスニペットを必要とする:

    //In Table Adapter  
    protected override void Dispose(bool disposing) 
    { 
        base.Dispose(disposing); 
    
        this.Adapter.Dispose(); 
    } 
    
  • TableAdapterDisposeコールの実装について:部分クラスでこれを入れた方が良いですので、データセットの再生成はこのコード(および追加する必要がある追加のコード)には影響しないことを確認してください。

+0

私はEntityフレームワークと最新の1.82.0 sqliteを使用して非常に大きなメモリリークが発生しています。これが問題だと思いますか? – Peter

+0

おそらく、(私はEFで希少な経験があると思うので)リリースされるのを待っている 'SQLiteCommand'に関連する管理されていないリソースがあるかもしれません。 [このSOスレッド](http://stackoverflow.com/questions/8511901/system-data-sqlite-close-not-releasing-database-file)と[this ticket](http://system.data.sqlite。 org/index.html/tktview?名前= 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 2つのdispose呼び出しの代わりに '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(); 
     } 
    } 
} 

次に、あなたは私が右TableAdapters /データセットを非難、代わりのたびに再生成されたTableAdapterののcodefileの変更についてedymttの答えを見つけ、この

connection.ExecuteScalar(commandText); 
+2

このような例外を飲み込むことを強くお勧めします! –

+1

同じ人、同じコメント、別の回答です。修正しました。 – Nate

+0

これはどのように例外を飲み込むのですか?これを使うと、コマンドが失敗した場合、usingステートメントの最後に例外がスローされます。 – Miebster

1

のようにそれを使用することができ、私は他の解決策を見つけました:TableAdapterの子要素の.Disposeを手動で呼び出します。 (.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/en-us/library/wa80x488 %28v = vs.110%29.aspx)。 System.Data.SQLiteの最新バージョンでは、私のコードはもはや必要ではないことを指摘してくれてありがとうございます([changelog](http://system.data.sqlite.org/index.html/doc/trunk/www/newsを参照)。 .wiki)リリース1.0.84用 – edymtt

1

ほとんどの場合、読者とコマンドを正しく処理しないと問題が発生します。コマンドと読者が適切に処理しないシナリオがあります。

シナリオ1:ブール関数を実行している場合。結果に達する前にfinallyブロック内のコードはエスケープされません。これは、結果iに合っている場合にコードを実行しているときにisDataExists関数の結果を評価しようとすると大きな問題になります。最後にただリーダーとコマンドを配置

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

を次のようにtryブロック内で読者やコマンドを処分:

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

ソリューション評価されている電子

if(isDataExists){ 
     // execute some code 
    } 

機能何かが間違っていた場合のために

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

'try'ブロックでコマンドを処理すると、' ExecuteReader() 'の実行中に例外が発生すると、コマンドは破棄されません。あなた自身で書くことを好むなら、複数の 'try' /' finally'ブロックを入れ子にすることができます。 –

0

これは私がこのエラーに遭遇したときに見つけたトップグーグルの結果の1つでした。しかし、私の周りをもっと調べてグーグルで検索した結果、どんなコードでも動作するこのコードを思いつきました。http://www.tsjensen.com/blog/post/2012/11/10/SQLite-on-Visual-Studio-with-NuGet-and-Easy-Instructions.aspx

しかし、私はNuGetをまったく使用する必要はありませんでした。私のプログラムは、開かれるたびにサーバーからdbファイルをダウンロードします。その後、ユーザーがその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 
       }; 
関連する問題