I have the following code to build an advanced data structure which is pulled from SQL Server, then when the retrevial of that data is complete I update the UI. The code used is
private void BuildSelectedTreeViewSectionAsync(TreeNode selectedNode)
{
// Initialise.
SqlServer instance = null;
SqlServer.Database database = null;
// Build and expand the TreeNode.
Task task = null;
task = Task.Factory.StartNew(() => {
string[] tmpStrArr = selectedNode.Text.Split(' ');
string strDatabaseName = tmpStrArr[0];
instance = SqlServer.Instance(this.conn);
database = instance.GetDatabaseFromName(strDatabaseName);
}).ContinueWith(cont => {
instance.BuildTreeViewForSelectedDatabase(this.customTreeViewSql,
selectedNode, database);
selectedNode.Expand();
task.Dispose();
}, CancellationToken.None, TaskContinuationOptions.OnlyOnRanToCompletion,
this.MainUiScheduler);
}
This works as it should on my main development machine; that is, it completes the build of the database object, then in the continuation update the UI and disposes the task (Task object).
However, I have been doing some testing on another machine and I get an InvalidOperationException, this is due to the task.Dispose() on task which still in the Running state, but the continuation cont should never fire unless the task has ran to completion.
Here’s what the code looks like in the debugger when the exception is thrown:

I am aware that it almost always unneccessary to call Dispose on tasks. This question is more about why the continuation is firing at all here?**
The reason for this is simple, you are calling
Disposeon the continuation itself and not on the first taskYour code consists of:
In the above code,
taskis equal to the continuation (ContinueWithdoesn’t pass back the originalTask, it passes the continuation) and that’s what’s getting captured in the closure you’re passing toContinueWith.You can test this by comparing the references of the
Taskparameter passed into theContinueWithmethod withtask:In order to dispose of the first, you need to break it up into two
Taskvariables and capture the firstTask, like so:However, because the
previous Taskis passed to you as a parameter in theContinueWithmethod, you don’t need to capturetaskin the closure at all, you can simply callDisposeon theTaskpassed as a parameter to you: