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

Dom*_*nas 3 c# sqlite

我已将我的全部包裹SQL Connections在一个using声明中。

\n\n
using (DbConnection dbConnection = GetConnection())\n{\n    using (DbCommand dbCommand = dbConnection.CreateCommand(cmdInsert))\n    {\n        //some work\n    }\n}\n
Run Code Online (Sandbox Code Playgroud)\n\n

因为UnitTests我应该使用 a :memory: database,但是database关闭连接后会自动删除。

\n\n

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

\n\n
\n

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

\n
\n\n

有没有解决方案如何使用 a:memory: database和 use using?我不想在没有..的情况下编写完全相同的代码两次using

\n\n

完整示例

\n\n

数据库

\n\n
public abstract class SqliteBase\n{\n    public string ConnectionString;\n\n    protected SqliteBase()\n    {      \n        SQLiteConnectionStringBuilder builder = new SQLiteConnectionStringBuilder\n        {\n            DataSource = ":memory:",\n            ForeignKeys = true,\n            DefaultTimeout = 3,\n            DateTimeKind = DateTimeKind.Utc,\n            Pooling = false             \n        };\n\n        ConnectionString = builder.ConnectionString + ";mode=memory;cache=shared";\n    }\n\n    private DbConnection _MemoryConnection;\n    protected DbConnection GetConnection()\n    {\n        try\n        {\n            if (_MemoryConnection == null)\n            {\n                _MemoryConnection = new SQLiteConnection(ConnectionString);                    \n                _MemoryConnection.Open();\n            }\n\n            DbConnection dbConnection = new SQLiteConnection(ConnectionString);\n            dbConnection.Open();\n\n            return dbConnection;\n        }\n\n        catch (Exception ex)\n        {\n            throw new Exception("Error opening database connection.", ex);\n        }\n    }\n\n    /// <summary>\n    /// Creates a table in the SQL database if it does not exist\n    /// </summary>\n    /// <param name="tableName">The name of the table</param>\n    /// <param name="columns">Comma separated column names</param>\n    protected void CreateTable(string tableName, string columns)\n    {\n        using (DbConnection dbConnection = GetConnection())\n        {\n            using (DbCommand dbCommand = dbConnection.CreateCommand($"create table if not exists {tableName} ({columns})"))\n            {\n                dbCommand.ExecuteNonQuery();\n            }\n        }\n    }\n}\n\npublic class FooDatabase : SqliteBase\n{\n    public FooDatabase()\n    {\n        CreateTable("FooTable", "Foo TEXT");\n    }\n\n    public void DoFoo()\n    {\n        using (DbConnection dbConnection = GetConnection())\n        {\n            using (DbCommand dbCommand = dbConnection.CreateCommand("Select * from FooTable"))\n            {\n                dbCommand.ExecuteNonQuery();\n            }\n        }\n    }\n}\n
Run Code Online (Sandbox Code Playgroud)\n\n

单元测试

\n\n
public static class SQLiteTestSetup\n{\n    public static FooDatabase ClassInit()\n    {\n       return new FooDatabase();\n    }\n\n    public static void Cleanup()\n    {\n\n    }\n}\n\npublic abstract class SQLiteTestBase\n{\n    public static FooDatabase Database { get; set; }\n\n    [TestMethod]\n    public void DoSomeFooTest()\n    {\n        Database.DoFoo();\n    }\n}\n\n[TestClass]\npublic class SQLiteTest : SQLiteTestBase\n{\n    [ClassInitialize]\n    public static void ClassInit(TestContext context)\n    {\n        Database = SQLiteTestSetup.ClassInit();\n    }\n\n    [ClassCleanup]\n    public static void ClassCleanup() => SQLiteTestSetup.Cleanup();\n}\n
Run Code Online (Sandbox Code Playgroud)\n\n

例外

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

Dom*_*nas 5

工作解决方案方法

我添加了一个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();
            }   
        }                        
    }
}
Run Code Online (Sandbox Code Playgroud)

单元测试

public abstract class SQLiteTestBase
{
    public static ConnectionContext Connection { get; set; }
    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 = new FooDatabase();            
        Database.GetContext = () => Connection;
        Connection = new ConnectionContext(Database.GetConnection(), false);            
    }

    [TestInitialize]
    public void TestInit()
    {
        Connection = new ConnectionContext(Database.GetConnection(), false);
        Database.Initialize();
    }

    [TestCleanup]
    public void TestCleanup()
    {
        Connection.Dispose();
        Connection = null;
    }
}
Run Code Online (Sandbox Code Playgroud)