SQLite:内存:​​使用/ dispose的数据库使用情况

时间:2018-02-22 10:48:51

标签: c# sqlite

我已将SQL Connections的所有using包裹在using (DbConnection dbConnection = GetConnection()) { using (DbCommand dbCommand = dbConnection.CreateCommand(cmdInsert)) { //some work } } 声明中。

UnitTests

对于:memory: database,我应该使用database,但在关闭连接后会自动删除:memory: database

https://www.sqlite.org/inmemorydb.html

  

当数据库的最后一个连接关闭时,将自动删除数据库并回收内存。

是否有解决方法如何使用using并使用using?我不想在没有public abstract class SqliteBase { public string ConnectionString; protected SqliteBase() { SQLiteConnectionStringBuilder builder = new SQLiteConnectionStringBuilder { DataSource = ":memory:", ForeignKeys = true, DefaultTimeout = 3, DateTimeKind = DateTimeKind.Utc, Pooling = false }; ConnectionString = builder.ConnectionString + ";mode=memory;cache=shared"; } private DbConnection _MemoryConnection; protected DbConnection GetConnection() { try { if (_MemoryConnection == null) { _MemoryConnection = new SQLiteConnection(ConnectionString); _MemoryConnection.Open(); } DbConnection dbConnection = new SQLiteConnection(ConnectionString); dbConnection.Open(); return dbConnection; } catch (Exception ex) { throw new Exception("Error opening database connection.", ex); } } /// <summary> /// Creates a table in the SQL database if it does not exist /// </summary> /// <param name="tableName">The name of the table</param> /// <param name="columns">Comma separated column names</param> protected void CreateTable(string tableName, string columns) { using (DbConnection dbConnection = GetConnection()) { using (DbCommand dbCommand = dbConnection.CreateCommand($"create table if not exists {tableName} ({columns})")) { dbCommand.ExecuteNonQuery(); } } } } public class FooDatabase : SqliteBase { public FooDatabase() { CreateTable("FooTable", "Foo TEXT"); } public void DoFoo() { using (DbConnection dbConnection = GetConnection()) { using (DbCommand dbCommand = dbConnection.CreateCommand("Select * from FooTable")) { dbCommand.ExecuteNonQuery(); } } } } ..

的情况下两次编写完全相同的代码

完成示例

数据库

public static class SQLiteTestSetup
{
    public static FooDatabase ClassInit()
    {
       return new FooDatabase();
    }

    public static void Cleanup()
    {

    }
}

public abstract class SQLiteTestBase
{
    public static FooDatabase Database { get; set; }

    [TestMethod]
    public void DoSomeFooTest()
    {
        Database.DoFoo();
    }
}

[TestClass]
public class SQLiteTest : SQLiteTestBase
{
    [ClassInitialize]
    public static void ClassInit(TestContext context)
    {
        Database = SQLiteTestSetup.ClassInit();
    }

    [ClassCleanup]
    public static void ClassCleanup() => SQLiteTestSetup.Cleanup();
}

单元测试

Die Testmethode "....SQLiteTest.DoSomeFooTest" hat eine Ausnahme ausgelöst: 
System.Data.SQLite.SQLiteException: SQL logic error
no such table: FooTable
    bei System.Data.SQLite.SQLite3.Prepare(SQLiteConnection cnn, String strSql, SQLiteStatement previous, UInt32 timeoutMS, String& strRemain)
   bei System.Data.SQLite.SQLiteCommand.BuildNextCommand()
   bei System.Data.SQLite.SQLiteCommand.GetStatement(Int32 index)
   bei System.Data.SQLite.SQLiteDataReader.NextResult()
   bei System.Data.SQLite.SQLiteDataReader..ctor(SQLiteCommand cmd, CommandBehavior behave)
   bei System.Data.SQLite.SQLiteCommand.ExecuteReader(CommandBehavior behavior)
   bei System.Data.SQLite.SQLiteCommand.ExecuteNonQuery(CommandBehavior behavior)
   bei System.Data.SQLite.SQLiteCommand.ExecuteNonQuery()
   bei ....FooDatabase.DoFoo() in ...\SqliteDatabaseBase.cs:Zeile 83.
   bei ....SQLiteTestBase.DoSomeFooTest() in ...\SQLiteTest.cs:Zeile 30.

异常

select anggota, 
sum(CASE WHEN origin = 'simp_pokok' THEN nilai END) `simp_pokok`,
sum(CASE WHEN origin = 'simp_wajib' THEN nilai END) `simp_wajib`,
sum(CASE WHEN origin = 'simp_sukarela' THEN nilai END) `simp_sukarela`,
sum(CASE WHEN origin = 'simp_berjangka' THEN nilai END) `simp_berjangka`
from (
    SELECT nama.anggota, sum(nilai) as nilai, origin
    FROM
    (SELECT anggota, nilai, 'simp_pokok' as origin from simp_pokok
     UNION ALL
     SELECT anggota, nilai, 'simp_wajib' as origin FROM simp_wajib
     UNION ALL
     SELECT anggota, nilai, 'simp_sukarela' as origin FROM simp_sukarela
     UNION ALL
     SELECT anggota, nilai, 'simp_berjangka' as origin FROM simp_berjangka
    ) nama
    group by nama.anggota, nama.origin) united
group by anggota

4 个答案:

答案 0 :(得分:1)

工作解决方案方法

我添加了一个def parse(self, response): # print response.xpath('//body//li/a/@href').extract() for item_url in response.xpath('//body//li/a/@href').extract(): item = UomscrapbotItem() item['url'] = item_url yield item 类,我可以设置一个标志来决定是否要处理ConnectionContext

数据库类

DbConnection

单元测试

public class ConnectionContext : IDisposable
{
    private readonly bool _ContextOwnsConnection;
    public readonly DbConnection Connection;

    public ConnectionContext(DbConnection connection, bool contextOwnsConnection)
    {
        Connection = connection;
        _ContextOwnsConnection = contextOwnsConnection;            
    }

    public void Dispose()
    {
        if(_ContextOwnsConnection)
            Connection.Dispose();
    }
}

public abstract class SqliteBase
{
    public Func<ConnectionContext> GetContext;

    private ConnectionContext _GetConnectionContext()
    {
        return new ConnectionContext(GetConnection(), true);
    }
    private string _ConnectionString;
    private readonly string _Dbfile;

    protected SqliteBase()
    {
        GetContext = _GetConnectionContext;
        _Dbfile = ":memory:";

        _InitConnectionString();
    }

    private void _InitConnectionString()
    {
        SQLiteConnectionStringBuilder builder = new SQLiteConnectionStringBuilder
        {
            DataSource = _Dbfile,
            ForeignKeys = true,
            DefaultTimeout = 3,
            DateTimeKind = DateTimeKind.Utc,
            Pooling = true
        };

        _ConnectionString = builder.ConnectionString;
    }

    public DbConnection GetConnection()
    {
        try
        {
            DbConnection dbConnection = SQLiteFactory.Instance.CreateConnection();                
            dbConnection.ConnectionString = _ConnectionString;
            dbConnection.Open();

            return dbConnection;
        }

        catch (Exception ex)
        {
            throw new Exception("Error opening database connection.", ex);
        }
    }

    /// <summary>
    /// Creates a table in the SQL database if it does not exist
    /// </summary>
    /// <param name="tableName">The name of the table</param>
    /// <param name="columns">Comma separated column names</param>
    protected void CreateTable(string tableName, string columns)
    {
        using (ConnectionContext context = GetContext())
        {
            using (DbCommand dbCommand = context.Connection.CreateCommand($"create table if not exists {tableName} ({columns})"))
            {
                dbCommand.ExecuteNonQuery();
            }   
        }                   
    }
}

public class FooDatabase : SqliteBase
{
    public FooDatabase()
    {
        Initialize();
    }

    public void Initialize()
    {
        CreateTable("FooTable", "Foo TEXT");
    }

    public void DoFoo()
    {
        using (ConnectionContext context = GetContext())
        {
            using (DbCommand dbCommand = context.Connection.CreateCommand("Select * from FooTable"))
            {
                dbCommand.ExecuteNonQuery();
            }   
        }                        
    }
}

答案 1 :(得分:0)

我猜你早就停止了阅读。您发布的链接的第二段“内存数据库和共享缓存”描述了您可以执行和需要执行的操作:

如果您使用所发布链接中描述的方法,则在关闭最后一个连接后删除数据库。

您需要一个文件URI,mode=memory用于内存 cache=shared,以便将多个连接转到同一个命名数据库。

因此,如果您的单元测试以一个名为连接的using语句开始(例如与您当前测试的名称相同),那么在该using语句中,使用相同连接字符串的所有连接都应该连接到同一个连接内存数据库。

答案 2 :(得分:0)

我在共享内存sqlite数据库时遇到了同样的问题。我的解决方案是将连接字符串设置为“ FullUri = file :: memory:?cache = shared”

答案 3 :(得分:0)

尝试为数据源添加名称,而不是:memory:

data source=testName;foreign keys=True;default timeout=3;datetimekind=Utc;pooling=False;mode=memory;cache=shared