尝试从SQLiteDataReader读取时遇到ObjectDisposedException

时间:2015-08-31 23:37:10

标签: c# sqlite

我正在尝试阅读存储的SQLiteDataReader对象。理论上,它“应该”起作用,因为对象在引用之前存储在变量中(并且在达到参考线之前不会出现错误),但也许我的想法是错误的。

我正在尝试将我的应用程序保持在一个整齐的分层架构中。因此,每个数据库表都有自己的C#类,它有自己的select,insert,update和delete方法;只有数据层知道如何与数据库等进行通信。

当我尝试创建一个所有数据层类都可以引用的静态SQLiteConnection对象(以便保持打开并最小化开销,如果有的话)时,我遇到了连接问题。因此,我尝试使用using块来确保每次需要访问数据库时都正确处理连接,并希望这不会导致性能问题。

基本上,这是我的DatabaseConnection类中处理基本查询执行的方法:

public SQLiteDataReader ExecuteQuery(string sql)
{
    SQLiteDataReader rdr = null;
    using(SQLiteConnection conn = new SQLiteConnection(ConnectionString))
    {
        conn.Open();
        SQLiteCommand cmd = conn.CreateCommand();
        cmd.CommandText = sql;
        rdr = cmd.ExecuteReader();
    }
    return rdr;
}

这是调用该方法的代码。我将使用Associate表的对象/记录作为示例。

public class Associate
{
    public int RowId { get; private set; }
    public int Id { get; set; }
    public string Name { get; set; }

    private string password;
    public string Password
    {
        get
        {
            return password;
        }
        set
        {
            password = Hash(value); // external password hashing method
        }
    }

    public Associate() { } // default constructor with default values

    public Associate(int id)
    {
        this.Id = id;
        Select();
    }

    // select, insert, update, delete methods
    private void Select() { ... }

    // these are non-queries and return true/false based on success
    public bool Insert() { ... }
    public bool Update() { ... }
    public bool Delete() { ... }

    /* Method that causes the error */
    public static Associate[] GetAll()
    {
        DatabaseConnection con = new DatabaseConnection();
        SQLiteDataReader rdr = con.ExecuteQuery("SELECT id FROM Associate");

        List<Associate> list = new List<Associate>();

        if (rdr != null)
        {
            while (rdr.Read()) /* this line throws the exception */
            {
                int next = rdr.GetInt32(0);
                list.Add(new Associate(next));
            }
        }

        return list.ToArray();
    }
}

这里的想法是使用rdr对象,我可以直接访问列名,这样如果数据库发生变化,我就不必重写一堆代码来调整列索引( rdr["id"]rdr["name"]等。)

所以我不明白为什么调用方法中的rdr有“对象处理”问题,因为它在我引用之前存储在变量中。我知道连接是在被调用方法的末尾处理的,但是由于存储了返回的结果,从技术上讲它是不是能够在using块之外“存活”?

1 个答案:

答案 0 :(得分:3)

这是被处置的连接。数据阅读器只能在连接仍然存在时读取数据。

public SQLiteDataReader ExecuteQuery(string sql)
{
    SQLiteDataReader rdr = null;
    using(SQLiteConnection conn = new SQLiteConnection(ConnectionString))
    {
        conn.Open();
        SQLiteCommand cmd = conn.CreateCommand();
        cmd.CommandText = sql;
        rdr = cmd.ExecuteReader();
    }
    // *** Connection gone at this stage ***
    return rdr;
}

您可以选择返回DataTable,例如

public DataTable ExecuteQuery(string sql)
{
    SQLiteDataReader rdr = null;
    using(SQLiteConnection conn = new SQLiteConnection(ConnectionString))
    {
        conn.Open();
        SQLiteCommand cmd = conn.CreateCommand();
        cmd.CommandText = sql;
        rdr = cmd.ExecuteReader();

        var dataTable = new DataTable();
        dataTable.Load(rdr);
        return dataTable;
    }
}

否则,您可以在DatabaseConnection类中保持连接活动:

class DatabaseConnection : IDisposable
{
    private readonly IDbConnection _conn;

    public DatabaseConnection() 
    {
       _conn = new SQLiteConnection(ConnectionString);
    }

    public void Dispose()
    {
       _conn.Dispose();
    }

    public SQLDataReader ExecuteQuery(string sql) 
    {
        ...
    }
}

// sample usage
using (var conn = new DatabaseConnection())
{
   using (var reader = conn.ExecuteQuery("SELECT ...")
   {
       // do your work in here
   }
}