我正在尝试使用.Net 4.5在Windows窗体应用程序中创建我的第一个TPL DataFlow服务。
概述,应用程序从.csv文本文件加载和解析一些数据,并在一些处理/询问后生成SQL数据库记录(使用实体框架数据访问层)。一旦加载了所有记录,应用程序就会通过迭代生成的数据库记录来生成结果文本文件。这是工作流程......
loadListFile: iterates through text file and posts to...
+ createStateRecord: generates the database records
+ setStateRecordStatus: updates the UI for each record
WhenAll records have been created...
generateReport: iterates through the database and posts to
+ writeReport: writes a summary record to a text file
+ setReportStatus: updates the UI to show report progress
我很欣赏我可以将这两个任务之间的关系构建为工作流程的一部分,但是现在我想将它们分开
我遇到的问题是应用程序/ TPL工作流似乎在第一次传递时正常工作,但如果重置UI并且第二次运行该进程,则永远不会调用writeReport.Completion方法 - 即使调用writeReport.Complete()方法。
这是完整的方法(从表单上的“click”偶数处理程序中调用)。
private async void ProcessData()
{
/********************************************************************/
/** Create State records */
/********************************************************************/
var createStateRecord = new TransformBlock
<CheckData, CheckResult>(checkData =>
{
return DalServices.CreateStateRecord(checkData);
}, new ExecutionDataflowBlockOptions
{
CancellationToken = _cancellationToken.Token,
MaxDegreeOfParallelism = 10
});
var setStateRecordStatus = new ActionBlock <CheckResult>(
result =>
{
Interlocked.Increment(ref _noRecordsProcessed);
if (!result.Success)
Interlocked.Increment(ref _noRecordsFailed);
pbarLoad.Minimum = 0;
pbarLoad.Maximum = _noRecordsInFile;
pbarLoad.Value = _noRecordsProcessed;
}, new ExecutionDataflowBlockOptions
{
CancellationToken = _cancellationToken.Token,
TaskScheduler = TaskScheduler
.FromCurrentSynchronizationContext()
});
var loadListFile = new ActionBlock<string>(
listFilePath =>
{
using (var reader =
new DataProviderService(_listFileInfo.FullName))
{
_noRecordsProcessed = 0;
_noRecordsInFile = reader.NoRows;
foreach (var item in reader)
{
createStateRecord.Post(new CheckData(item));
}
createStateRecord.Complete();
}
},
new ExecutionDataflowBlockOptions
{
CancellationToken = _cancellationToken.Token
});
/*
* Link StateRecord tasks
*/
createStateRecord.LinkTo(setStateRecordStatus);
createStateRecord.Completion.ContinueWith(t =>
{
if (t.IsFaulted)
{
((IDataflowBlock) setStateRecordStatus)
.Fault(t.Exception);
return;
}
setStateRecordStatus.Complete();
}, _cancellationToken.Token);
/********************************************************************/
/** Reporting tasks */
/********************************************************************/
var writeReport = new TransformBlock<ReportData, State>(
reportData =>
{
using (
var writer = new StreamWriter(
reportData.ResultsFilePath, true))
{
writer.WriteLine(reportData.State.ToString());
}
return reportData.State;
},
new ExecutionDataflowBlockOptions
{
CancellationToken = _cancellationToken.Token,
MaxDegreeOfParallelism = 1
});
var setReportStatus = new ActionBlock<State>(
result =>
{
Interlocked.Increment(ref _noRecordsReported);
pbarReport.Minimum = 0;
pbarReport.Maximum = _noRecordsInFile;
pbarReport.Value = _noRecordsReported;
},
new ExecutionDataflowBlockOptions
{
CancellationToken = _cancellationToken.Token,
TaskScheduler = TaskScheduler
.FromCurrentSynchronizationContext()
});
var generateReport = new ActionBlock<string>(
listFilePath =>
{
using (var stateService = DalService.GetStateService())
{
var resultsFilePath = listFilePath + ".results";
foreach (var state in stateService.GetStates())
{
writeReport.Post(
new ReportData
{
State = state,
ResultsFilePath = resultsFilePath
});
}
// This alwaus gets called
writeReport.Complete();
}
},
new ExecutionDataflowBlockOptions
{
CancellationToken = _cancellationToken.Token
});
/*
* Link Reporting tasks
*/
writeReport.LinkTo(setReportStatus);
writeReport.Completion.ContinueWith(t =>
{
// This only get called on first run!
if (t.IsFaulted)
{
((IDataflowBlock) setStateRecordStatus).Fault(
t.Exception);
return;
}
setReportStatus.Complete();
}, _cancellationToken.Token);
/********************************************************************/
/** Run the tasks */
/********************************************************************/
try
{
loadListFile.Post(_listFileInfo.FullName);
loadListFile.Complete();
await Task.WhenAll(createStateRecord.Completion);
generateReport.Post(_listFileInfo.FullName);
generateReport.Complete();
await Task.WhenAll(writeReport.Completion);
}
catch (TaskCanceledException)
{
MessageBox.Show(
"Job cancelled by user.", "Job Cancelled",
MessageBoxButtons.OK);
SetUiAfterProcessing("Job cancelled by user.");
}
catch (AggregateException aex)
{
MessageBox.Show(
aex.ListExceptions(), "Task Errors", MessageBoxButtons.OK);
SetUiAfterProcessing("Task processing error - job cancelled.");
}
catch (Exception ex)
{
MessageBox.Show(
ex.ToString(), "Unhandled Exception", MessageBoxButtons.OK);
SetUiAfterProcessing("Application error - job cancelled.");
}
finally
{
SetUiAfterProcessing("Job complete.");
}
}
我尝试过重构方法并简化每个Block的内部操作,但我仍然没有更接近于确定我做错了什么 - 任何帮助都会非常感激。谢谢。