Я отправляю серию select
операторов (запросов - тысячи из них) в одну базу данных синхронно и получаю один DataTable
на запрос (Примечание: эта программа такова, что она знает схему БД, которую она сканирует, только во время выполнения , отсюда и использование DataTables
). Программа запускается на клиентской машине и подключается к БД на удаленной машине. Выполнение такого количества запросов занимает много времени. Итак, предполагая, что их асинхронное или параллельное выполнение ускорит процесс, я изучаю TPL Dataflow (TDF)
. Я хочу использовать библиотеку TDF
, потому что она решает все проблемы, связанные с написанием многопоточного кода, который в противном случае пришлось бы делать вручную.
Показанный код основан на http://blog.i3arnon.com/2016/05/23/tpl-dataflow/. Он минимален и предназначен только для того, чтобы помочь мне понять основные операции TDF
. Пожалуйста, знайте, что я прочитал много блогов и закодировал много итераций, пытаясь взломать этот орех.
Тем не менее, с этой текущей итерацией у меня есть одна проблема и вопрос:
Проблема
Код находится внутри метода button click
(используя пользовательский интерфейс, пользователь выбирает машину, экземпляр sql и базу данных, а затем запускает сканирование). Две строки с оператором await
возвращают ошибку во время сборки: The 'await' operator can only be used within an async method. Consider marking this method with the 'async' modifier and changing its return type to 'Task'
. Я не могу изменить возвращаемый тип метода нажатия кнопки. Нужно ли как-то изолировать метод button click
от кода async-await
?
Вопрос
Несмотря на то, что я нашел статьи, описывающие основы TDF
, я не могу найти пример того, как получить в свои руки вывод, который производит каждый вызов TransformBlock
(т. е. DataTable
). Хотя я хочу отправить запросы async
, мне нужно заблокировать их до тех пор, пока все запросы, отправленные в TransformBlock
, не будут выполнены. Как мне получить серию DataTable
, созданных TransformBlock
, и заблокировать, пока все запросы не будут выполнены?
Примечание. Я признаю, что сейчас у меня есть только один блок. Как минимум, я добавлю блокировку отмены, поэтому мне нужно/хочется использовать TPL.
private async Task ToolStripButtonStart_Click(object sender, EventArgs e)
{
UserInput userInput = new UserInput
{
MachineName = "gat-admin",
InstanceName = "",
DbName = "AdventureWorks2014",
};
DataAccessLayer dataAccessLayer = new DataAccessLayer(userInput.MachineName, userInput.InstanceName);
//CreateTableQueryList gets a list of all tables from the DB and returns a list of
// select statements, one per table, e.g., SELECT * from [schemaname].[tablename]
IList<String> tableQueryList = CreateTableQueryList(userInput);
// Define a block that accepts a select statement and returns a DataTable of results
// where each returned record is: schemaname + tablename + columnname + column datatype + field data
// e.g., if the select query returns one record with 5 columns, then a datatable with 5
// records (one per field) will come back
var transformBlock_SubmitTableQuery = new TransformBlock<String, Task<DataTable>>(
async tableQuery => await dataAccessLayer._SubmitSelectStatement(tableQuery),
new ExecutionDataflowBlockOptions
{
MaxDegreeOfParallelism = 2,
});
// Add items to the block and start processing
foreach (String tableQuery in tableQueryList)
{
await transformBlock_SubmitTableQuery.SendAsync(tableQuery);
}
// Enable the Cancel button and disable the Start button.
toolStripButtonStart.Enabled = false;
toolStripButtonStop.Enabled = true;
//shut down the block (no more inputs or outputs)
transformBlock_SubmitTableQuery.Complete();
//await the completion of the task that procduces the output DataTable
await transformBlock_SubmitTableQuery.Completion;
}
public async Task<DataTable> _SubmitSelectStatement(string queryString )
{
try
{
.
.
await Task.Run(() => sqlDataAdapter.Fill(dt));
// process dt into the output DataTable I need
return outputDt;
}
catch
{
throw;
}
}