Reputation: 21
I'm playing with tasks, and I don't know how to cancel task when Exception is thrown. I am trying with TaskCompletionSourcecancel
but it doesn't work.
Is there a way to completely halt the task when Exception is thrown?
public async Task Migrate()
{
var tcs = new TaskCompletionSource<bool>();
if (IsDataMigratorItemParameteresCorrect())
{
try
{
using (SqlConnection srcconnection = this.SourceConnection)
{
await srcconnection.OpenAsync();
using (SqlCommand srccmd = CreateMigrateTaskCommand(this.SourceForHash, srcconnection))
{
using (SqlDataReader srcreader = await srccmd.ExecuteReaderAsync(CommandBehavior.SequentialAccess))
{
using (SqlConnection destconnection = this.DestinationConnection)
{
await destconnection.OpenAsync();
await CreateStageTable(destconnection, srcreader);
await BulkCopy(destconnection, srcreader);
await Merge(destconnection, srcreader);
await DropTable(destconnection);
}
}
}
}
}
catch (Exception ex)
{
Console.WriteLine(DestinationCommand + " canceled\nException : " + ex.Message);
tcs.SetCanceled();
}
}
}
private SqlCommand CreateMigrateTaskCommand(string commandQuery, SqlConnection connection)
{
SqlCommand scmd = new SqlCommand(commandQuery, connection);
return scmd;
}
private async Task Merge(SqlConnection conn, SqlDataReader reader)
{
using (SqlCommand cmdMerge = conn.CreateCommand())
{
string mergeQuery = SqlGenerator.BuildMergeQuery(reader, DestinationCommand, this.SourceUniqueKey, this.DestinationUniqueKey);
cmdMerge.CommandText = mergeQuery;
cmdMerge.CommandType = CommandType.Text;
cmdMerge.CommandTimeout = 0;
await cmdMerge.ExecuteNonQueryAsync();
Console.WriteLine("{0} - MERGE START\n", DestinationCommand);
}
Console.WriteLine("{0} - MERGE SUCCESS!\n", DestinationCommand);
}
private async Task BulkCopy(SqlConnection conn, SqlDataReader reader)
{
using (SqlBulkCopy bcp = new SqlBulkCopy(conn.ConnectionString, SqlBulkCopyOptions.TableLock)) //table lock
{
bcp.SqlRowsCopied += bcp_SqlRowsCopied;
bcp.BatchSize = this.BatchSize;
bcp.EnableStreaming = true;
bcp.BulkCopyTimeout = 0;
bcp.NotifyAfter = 1000;
string destTable = this.DestinationCommand + "_stage";
bcp.DestinationTableName = destTable;
await bcp.WriteToServerAsync(reader);
}
}
private async Task CreateStageTable(SqlConnection conn, SqlDataReader reader)
{
using (SqlCommand cmdCreateTable = conn.CreateCommand())
{
try
{
string createTableQuery = SqlGenerator.BuildCreateTableQuery(reader, this.DestinationCommand);
cmdCreateTable.CommandText = createTableQuery;
cmdCreateTable.CommandType = CommandType.Text;
await cmdCreateTable.ExecuteNonQueryAsync();
Console.WriteLine(this.DestinationCommand + " created!");
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
}
}
private async Task DropTable(SqlConnection conn)
{
using (SqlCommand cmdCreateTable = conn.CreateCommand())
{
try
{
string tableName = DestinationCommand + "_stage";
var builder = new SqlCommandBuilder();
string escapedTableName = builder.QuoteIdentifier(tableName);
cmdCreateTable.CommandText = "drop table " + escapedTableName;
Console.WriteLine(escapedTableName + " created!");
}
catch (Exception ex)
{
Console.WriteLine(DestinationCommand + " canceled\nException : " + ex.Message);
tcs.SetCanceled();
}
}
}
Upvotes: 2
Views: 1160
Reputation: 457217
There are two parts to cancellation: requesting the cancellation (TaskCompletionSource.SetCanceled
) and responding to that cancellation request.
Your code is calling SetCanceled
when an exception occurs, which is OK if you want to cancel other operations when that one has an exception.
However, nothing in the code is listening to the cancellation token. It should be passed to APIs that support CancellationToken
, or periodically checked (CancellationToken.ThrowIfCancellationRequested
), or have a cancellation callback attached to it (CancellationToken.Register
). Otherwise, it's just a flag that is set and no one is checking it.
Upvotes: 4