简体   繁体   中英

How to dispose SQLiteCommands created internally by Entity Framework?

I have problems with System.Data.SQLite (v1.0.94.0) writing "SQLite error (5): database is locked" to the log when opening a new connection. This seems to be caused by Entity Framework (v6.0) not disposing all the SQLiteCommands.

I am using code first with Entity Framework and can therefore not use this .

One scenario, where the SQLiteCommand is not disposed, is using the IQueryable.

IQueryable<TEntity> query = context.Set<TEntity>();
var result = query.ToList();

How do I keep track of these undisposed commands, so I can dispose them manually?

I would like to avoid calling GC.Collect();

This discussion seems to say that defining interop legacy settings is a workaround (instead of going back to an older version that works... http://www.mail-archive.com/sqlite-users%40sqlite.org/msg75310.html

" The root cause in this case is that the Entity Framework indirectly creates a SQLiteCommand object and then subsequently fails to dispose it. Furthermore, it does not appear to expose these internally created commands, nor a way to explicitly dispose of them, leaving no means for an outside caller to cleanup. This seems quite strange since almost all IDbCommand implementations "in the wild" would likely require native resources of one kind or another. Also, even the DbCommand base class provided by the .NET Framework itself implements IDisposable (ie the class used as the base class for SQLiteCommand). "

" Yes, the InteropLegacyClose MSBuild property in the "SQLite.NET.Settings.targets" file needs to be set to "true" and the define "INTEROP_LEGACY_CLOSE=1;" needs to be added to the INTEROP_EXTRA_DEFINES property in the SQLite.Interop.20XX.[vs]props file for the version of Visual Studio being used. "

UPDATE: dotConnect for SQLLite looks the business http://www.devart.com/dotconnect/sqlite/ there is a free and pro version which isn't free... might be worth checking it out.

UPDATE2: In response to " If this was a bug with EF, everyone that uses it would have issues even when targeting SQL Server. – Panagiotis Kanavos"

Not at all, it seems the bug is only reported by SQLLite users around 1.0.8xxx where they changed their dispose methods... the EF provider can vary, eg you could set it to SQLLite (bug reported) or SQLServer (bug not reported) etc like below...

<system.data>
  <DbProviderFactories>
    <add name="SQLite Data Provider"
          invariant="System.Data.SQLite"
          description="Data Provider for SQLite"
          type="System.Data.SQLite.SQLiteFactory, System.Data.SQLite" />
  </DbProviderFactories>
</system.data>

or

<providers> 
  <provider invariantName="System.Data.SqlClient" type="System.Data.Entity.SqlServer.SqlProviderServices, EntityFramework.SqlServer" /> 
</providers>

Oliver Wickenden posted an effective workaround in this thread: System.Data.SQLite Close() not releasing database file

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

He says: To use just call ClearSQLiteCommandConnectionHelper.Initialise(); at the start of application load. This will then keep a list of active commands and will set their Connection to Null when they point to a connection that is closed.

This worked a treat for me.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM