jeudi 16 juin 2016

SQLite Database is Locked when running two specific unit tests

I'm at a complete loss here. I have two tests: Update_Task and Get_All_Tasks. When run individually, they pass. If I run them together in Visual Studio 2015 (not in parallel -- just selecting them both and clicking run) Update_Task passes and Get_All_Tasks does not. For the latter, I get an error message saying that the database is locked. This implies that Update_Task locks the database and fails to release the lock, but I can't figure out where that would do it.

Any thoughts? Code follows.


EDIT: I found the answer. It turns out that when you do a var reader = cmd.ExecuteReader(), that reader needs to be put in a using block itself. Otherwise, the reader may not close and that caused the lock. Doing this fixed the problem:

using (var reader = cmd.ExecuteReader())
{
    // The query will return only one item, so read it into a new task.
    reader.Read();
    return this.GetTaskFromReader(reader);
}

Test Methods

[TestMethod]
public void Update_Task()
{
    var dbMan = new DatabaseManager();

    // Add a new task
    var t = new Task()
    {
        IsEnabled = true,
        Description = "Test",
        DueDate = DateTime.Now,
        Priority = Priority.High
    };

    dbMan.InsertTask(t);

    // Alter the task and update it
    t.IsEnabled = false;
    t.Description = "Updated test";
    t.DueDate = DateTime.Now.AddDays(1);
    t.Priority = Priority.Medium;
    var updatedTask = this.UpdateTaskAndRetrieve(dbMan, t);

    Assert.AreEqual(t, updatedTask, "Task did not update correctly.");
}

[TestMethod]
public void Get_All_Tasks()
{
    var dbMan = new DatabaseManager();

    // Get the number of tasks currently in the database
    var originalCount = dbMan.GetAllTasks().Count;

    // Add 5 tasks with different values
    for (int i = 0; i < 5; i++)
    {
        var t = new Task()
        {
            IsEnabled = i % 2 == 0,
            Description = $"Task {i + 1}",
            DueDate = DateTime.Now.AddDays(i),
            Priority = (Priority)(i % 4)
        };

        dbMan.InsertTask(t); // <-- Fails when i == 0
    }

    // Get all the tasks in the database.
    var result = dbMan.GetAllTasks();
    Assert.AreEqual(originalCount + 5, result.Count, "Wrong number of tasks came back from the database.");
}

private Task UpdateTaskAndRetrieve(DatabaseManager dbMan, Task t)
{
    dbMan.UpdateTask(t);
    return dbMan.GetTask(t.Id);
}

DatabaseManager Methods

public void UpdateTask(Task task)
{
    if (task.Id == 0)
    {
        throw new InvalidOperationException("Cannot update a task that has not yet been inserted.");
    }

    using (var connection = new SQLiteConnection(_connectionString))
    {
        connection.Open();
        using (var cmd = new SQLiteCommand(connection))
        {
            var sql = @"UPDATE [Task] 
                SET 
                    [enabled]=@enabled,
                    [description]=@description,
                    [duedate]=@duedate,
                    [priority]=@priority
                WHERE rowid=@rowId";

            // Build up the command and its parameters
            cmd.CommandType = CommandType.Text;
            cmd.CommandText = sql;
            cmd.Parameters.Add(new SQLiteParameter("@enabled", task.IsEnabled ? 1 : 0));
            cmd.Parameters.Add(new SQLiteParameter("@description", task.Description));
            cmd.Parameters.Add(new SQLiteParameter("@duedate", task.DueDate.Ticks));
            cmd.Parameters.Add(new SQLiteParameter("@priority", (int)task.Priority));
            cmd.Parameters.Add(new SQLiteParameter("@rowid", task.Id));

            var result = cmd.ExecuteNonQuery();
            Debug.WriteLine($"Command executed.  {result} rows affected.");
        }
    }
}

public IList<Task> GetAllTasks()
{
    using (var connection = new SQLiteConnection(_connectionString))
    {
        connection.Open();
        using (var cmd = new SQLiteCommand(connection))
        {
            // Create the command
            var sql = "SELECT rowid, * from [Task] ORDER BY [duedate] DESC, [priority] DESC";
            cmd.CommandText = sql;

            // Execute the command and build the resultant list
            var reader = cmd.ExecuteReader();
            var retList = new List<Task>();
            while (reader.Read())
            {
                retList.Add(this.GetTaskFromReader(reader));
            }

            return retList;
        }
    }
}

public Task GetTask(int id)
{
    using (var connection = new SQLiteConnection(_connectionString))
    {
        connection.Open();
        using (var cmd = new SQLiteCommand(connection))
        {
            var sql = "SELECT rowid, * from [Task] WHERE rowid=@id";
            cmd.CommandText = sql;
            cmd.Parameters.Add(new SQLiteParameter("@id", id));
            var reader = cmd.ExecuteReader();

            // The query will return only one item, so read it into a new task.
            reader.Read();
            var task = this.GetTaskFromReader(reader);
            return task;
        }
    }
}

private Task GetTaskFromReader(SQLiteDataReader reader)
{
    var retId = Convert.ToInt32(reader["rowid"]);
    var desc = reader["description"].ToString();
    var date = new DateTime((long)reader["duedate"]);
    var isEnabled = Convert.ToBoolean(reader["enabled"]);
    var priority = (Priority)Enum.Parse(typeof(Priority), reader["priority"].ToString());

    return new Task(retId, isEnabled, desc, date, priority);
}

Aucun commentaire:

Enregistrer un commentaire