ฉันกำลังส่งชุดคำสั่ง select
(แบบสอบถาม - หลายพันชุด) ไปยังฐานข้อมูลเดียวพร้อมกันและรับกลับหนึ่ง DataTable
ต่อการสืบค้น (หมายเหตุ: โปรแกรมนี้มีความรู้เกี่ยวกับสคีมา DB ที่กำลังสแกนเฉพาะในเวลารันไทม์เท่านั้น ดังนั้นการใช้ DataTables
) โปรแกรมทำงานบนเครื่องไคลเอนต์และเชื่อมต่อกับฐานข้อมูลบนเครื่องระยะไกล ใช้เวลานานในการเรียกใช้แบบสอบถามจำนวนมาก ดังนั้น สมมติว่าการดำเนินการแบบอะซิงก์หรือแบบขนานจะทำให้สิ่งต่าง ๆ เร็วขึ้น ฉันกำลังสำรวจ TPL Dataflow (TDF)
ฉันต้องการใช้ไลบรารี TDF
เพราะดูเหมือนว่าจะจัดการกับข้อกังวลทั้งหมดที่เกี่ยวข้องกับการเขียนโค้ดแบบมัลติเธรดที่อาจจำเป็นต้องทำด้วยมือ
รหัสที่แสดงขึ้นอยู่กับ http://blog.i3arnon.com/2016/05/23/tpl-dataflow/. มันน้อยที่สุดและเป็นเพียงเพื่อช่วยให้ฉันเข้าใจการทำงานพื้นฐานของ TDF
โปรดทราบว่าฉันได้อ่านบล็อกมากมายและเขียนโค้ดซ้ำหลายครั้งเพื่อพยายามถอดรหัสถั่วนี้
ไม่น้อยไปกว่านั้น ด้วยการวนซ้ำปัจจุบันนี้ ฉันมีปัญหาหนึ่งข้อและคำถาม:
ปัญหา
โค้ดอยู่ในเมธอด button click
(เมื่อใช้ UI ผู้ใช้จะเลือกเครื่อง อินสแตนซ์ 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
หรือไม่
คำถาม
แม้ว่าฉันจะพบบทความเขียนแบบ beau-coup ที่อธิบายพื้นฐานของ TDF
แต่ฉันไม่พบตัวอย่างวิธีจัดการกับผลลัพธ์ที่การเรียกใช้ TransformBlock
แต่ละครั้งสร้างขึ้น (เช่น DataTable
) แม้ว่าฉันต้องการส่งข้อความค้นหา async
แต่ฉันจำเป็นต้องบล็อกจนกว่าข้อความค้นหาทั้งหมดที่ส่งไปยัง TransformBlock
จะเสร็จสมบูรณ์ ฉันจะได้เป็นเจ้าของซีรีส์ DataTable
s ที่สร้างโดย 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;
}
}