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