我使用 ContinueWith 运行一个任务,该任务具有双重职责:如果任务成功完成,则处理结果,或者如果确实发生错误,则处理任何异常。但是下面的代码不会正确处理任何异常,它会注册为未处理并关闭程序(为了发布而有所缩短,因此可能并不完美):
void _SqlServerDatabaseListLoader()
{
_ClearSqlHolders(true, false);
_SqlConnectionStringHolder.Database = "master";
if (_SqlConnectionStringHolder.IsComplete)
{
//Could time out put on its own thread with a continuation back on the UI thread for the popup
_TaskCanceller = new CancellationTokenSource();
_TaskLoader = Task.Factory.StartNew(() =>
{
IsLoadingSqlServerDatabaseList = true;
using (SqlConnection con = new SqlConnection(_SqlConnectionStringHolder))
{
// Open connection
con.Open(); //If this cause an error (say bad password) the whole thing bombs
//create a linq connection and get the list of database names
DataContext dc = new DataContext(con);
return new ObservableCollection<string>(dc.ExecuteQuery<string>("select [name] from sys.databases").ToObservableCollection());
}
}).ContinueWith(antecendant => _SqlServerDatabaseListLoaderComplete(antecendant.Result, antecendant.Exception),
_TaskCanceller.Token,
TaskContinuationOptions.None,
TaskScheduler.FromCurrentSynchronizationContext());
}
}
void _SqlServerDatabaseListLoaderComplete(ObservableCollection<string> DatabaseList, AggregateException ae)
{
//Just show the first error
if (ae != null)
ToolkitDialog.ShowException(ae.InnerExceptions[0], ToolkitDialogType.Error, CustomDialogButtons.OK, "Error:", "Database List Error");
if(DatabaseList != null)
SqlServerDatabaseList = DatabaseList
//Set the running indicator
_TaskLoader = null;
_TaskCanceller = null;
IsLoadingSqlServerDatabaseList = false;
}
我正在使用 TaskContinuationOptions.None 来解决这个问题,我认为这是正确的。这在上面的类继承自的基类中声明:
protected Task _TaskLoader;
protected CancellationTokenSource _TaskCanceller;
如果我在一个不会导致错误的场景下运行,一切都会很好,我会得到我的数据库列表。但如果出现错误,比如有人为此 SQL Server 登录凭据提供了错误密码,则不会处理该错误。
但是,如果我删除了传递 Result 参数的选项,一切都会正常运行,并且会捕获异常:
void _SqlServerDatabaseListLoader()
{
_ClearSqlHolders(true, false);
_SqlConnectionStringHolder.Database = "master";
if (_SqlConnectionStringHolder.IsComplete)
{
//Could time out put on its own thread with a continuation back on the UI thread for the popup
_TaskCanceller = new CancellationTokenSource();
_TaskLoader = Task.Factory.StartNew(() =>
{
IsLoadingSqlServerDatabaseList = true;
using (SqlConnection con = new SqlConnection(_SqlConnectionStringHolder))
{
// Open connection
con.Open();
//create a linq connection and get the list of database names
DataContext dc = new DataContext(con);
//HAVE TO SET IN THE THEAD AND NOT RETURN A RESULT
SqlServerDatabaseList = new ObservableCollection<string>(dc.ExecuteQuery<string>("select [name] from sys.databases").ToObservableCollection());
}
}).ContinueWith(antecendant => _SqlServerDatabaseListLoaderComplete(antecendant.Exception),
_TaskCanceller.Token,
TaskContinuationOptions.None,
TaskScheduler.FromCurrentSynchronizationContext());
}
}
void _SqlServerDatabaseListLoaderComplete(AggregateException ae)
{
//Just show the first error
if (ae != null)
ToolkitDialog.ShowException(ae.InnerExceptions[0], ToolkitDialogType.Error, CustomDialogButtons.OK, "Error:", "Database List Error");
//Set the running indicator
_TaskLoader = null;
_TaskCanceller = null;
IsLoadingSqlServerDatabaseList = false;
}
我假设我没有完全理解 TPL 是如何工作的。我尝试创建一个以上的 ContinueWith,但这似乎并没有什么不同。谢谢你的帮助。