Witam,
robię migrację danych z jednego serwera na inny i natrafiłem na pewien problem. Mam listę tasków, które odpalam asynchronicznie. Chciałbym zakończyć taska, jeśli w którejkolwiek z wywoływanych przez niego funkcji wyłapię exception.

Potrafię wykorzystać CancellationToken tworząc CancellationTokenSource i wywołać SetCancel a następnie throwifcancellationrequested.

Niestety umiem to tylko wykorzystać to tylko "z góry do dołu", tzn z poziomu Task Migrate() zrobić cancel na await CreateStageTable() itd.

Potrzebuję jednak wykonać podobną operację w drugą stronę, tj. w przypadku wystąpienia błędu w CreateStageTable() czy DropTable() natychmiastowo zakończyć pracę Taska Migrate().

Pozdrawiam,
mayw92

klasa MigratorItem

public class MigratorItem
{
//...
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();
                                    if (DoesTableExist(destconnection, this.DestinationCommand + "_stage"))
                                    {
                                        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();
                }
            }
        }
//...
}

Funkcje wywoływane z Taska

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(ex.Message);
                }
            }
        }

Klasa Migrator w której mam listę tasków i wywołuje je z pętli Pararell.Foreach.

class Migrator
{
 //...
	List<MigratorItem> migratorItems = new List<MigratorItem>();
	Ilist<Task> il = new List<Task>();
	
	Pararell.ForEach(migratorItems, t => il.Add(t.Migrate()));
//...
}