在我的代码中,我单击LoadTemplated按钮从excel下载数据。 我想在用户单击“取消”按钮后立即取消下载。 任务以某种方式没有在继续进行时被取消。 在这里我需要做些什么建议。
public CheckListDetailViewModel(IAuditInspectionDataService auditInspectionDataService)
{
_auditInspectionDataService = auditInspectionDataService;
LoadChecklistTemplateCommand = new DelegateCommand(OnLoadTemplate, CanLoadTemplate).ObservesProperty(() => ChecklistItems.Count);
}
private CancellationTokenSource cts;
private async void OnLoadTemplate()
{
try
{
if (cts != null)
{
cts.Cancel();
}
else
{
cts = new CancellationTokenSource();
IsBusy = true;
LoadTemplateButtonValue = "Cancel";
var items = await Task.Run(() =>
{
if (cts.Token.IsCancellationRequested)
cts.Token.ThrowIfCancellationRequested();
var checklistItems = _auditInspectionDataService.GetCheckList(InspectionType.InspectionTypeId);
return checklistItems;
}, cts.Token);
LoadTemplateButtonValue = "Load Template";
ChecklistItems = new ObservableCollection<ChecklistItem>(items);
}
}
catch (Exception ex)
{
IsBusy = false;
LoadTemplateButtonValue = "Load Template";
ChecklistItems = null;
cts = null;
Debug.WriteLine(ex);
}
finally
{
IsBusy = false;
LoadTemplateButtonValue = "Load Template";
//cts.Dispose();
}
}
答案 0 :(得分:0)
取消Task
时会引发OperationCanceledException
异常。捕获此异常的catch块应该是在处置已取消实例之后唯一创建CancellationTokenSource
新实例的地方。 CancellationTokenSource
只能使用一次(通过调用CancellationTokenSource.IsCancellationRequested
将true
设置为Cancel()
之后)。
仅当调用Task
并且请求取消时,才能取消CancellationToken.ThrowIfCancellationRequested()
。这意味着CancellationToken
的观察者负责在每次运行操作允许中止但尽可能频繁地中止CancellationToken.ThrowIfCancellationRequested()
时调用,以便尽快取消请求并始终在分配资源之前取消操作。
因此,只有将当前CancellationToken
的引用传递给在可取消Task
执行期间所调用的每个方法才有意义。还要始终确保所有async
方法都返回Task
或Task<T>
(事件处理程序除外,该事件处理程序需要根据void
委托签名返回event
) 。以下示例将按预期工作。
MainWindow.xaml
<Window>
<Window.DataContext>
<CheckListDetailViewModel />
</Window.DataContext>
<Button>
<Button.Style>
<Style>
<Setter Property="Command" Value="{Binding LoadChecklistTemplateCommand}" />
<Setter Property="Content" Value="Load Template" />
<!-- Set the Button's Command to CancelCommand once the download has started -->
<Style.Triggers>
<DataTrigger Binding="{Binding IsBusy}" Value="True">
<Setter Property="Content" Value="Cancel" />
<Setter Property="Command" Value="{Binding CancelCommand}" />
</DataTrigger>
</Style.Triggers>
</Style>
</Button.Style>
</Button>
</Window>
CheckListDetailViewModel.cs
public CancellationTokenSource CancellationTokenSource { get; set; }
private bool isBusy;
public bool IsBusy
{
get => this.isBusy;
set
{
this.isBusy = value;
OnPropertyChanged();
}
}
public ICommand LoadChecklistTemplateCommand { get; set; }
public ICommand CancelDownloadCommand => new DelegateCommand(CancelDownload, () => true);
public CheckListDetailViewModel(IAuditInspectionDataService auditInspectionDataService)
{
this.CancellationTokenSource = new CancellationTokenSource();
_auditInspectionDataService = auditInspectionDataService;
LoadChecklistTemplateCommand = new DelegateCommand(OnLoadTemplate, CanLoadTemplate).ObservesProperty(() => ChecklistItems.Count);
}
// Cancel Task instance
public void CancelDownload()
{
this.CancellationTokenSource.Cancel();
}
private async Task OnLoadTemplateAsync()
{
if (this.IsBusy)
{
return;
}
CancellationToken cancellationToken = this.CancellationTokenSource.Token;
this.IsBusy = true;
try
{
var items = await Task.Run(() =>
{
cancellationToken.ThrowIfCancellationRequested();
return this._auditInspectionDataService.GetCheckList(cancellationToken, InspectionType.InspectionTypeId);
}, cancellationToken);
this.ChecklistItems = new ObservableCollection<ChecklistItem>(items);
}
catch (OperationCanceledException)
{
// CancellationTokenSource can only be used once. Therefore dispose and create new instance
this.CancellationTokenSource.Dispose();
this.CancellationTokenSource = new CancellationTokenSource();
this.ChecklistItems = new ObservableCollection<ChecklistItem>();
}
this.IsBusy = false;
}
AuditInspectionDataService.cs
// Member of IAuditInspectionDataService
public void GetCheckList(CancellationToken cancellationToken)
{
using (OleDbConnection connection = new OleDbConnection("Provider=Microsoft.ACE.OLEDB.12.0;Data Source=\"myfile.xls\";Extended Properties=\"Excel 12.0;HDR=YES;IMEX=1\""))
{
OleDbCommand command = new OleDbCommand("SELECT * FROM [Sheet1$]", connection);
connection.Open();
OleDbDataReader reader = command.ExecuteReader();
int partitionSize = 50;
int linesRead = 0;
while (reader.Read())
{
if (++linesRead == partitionSize)
{
cancellationToken.ThrowIfCancellationRequested();
llnesRead = 0;
}
Console.WriteLine(reader[0].ToString());
}
reader.Close();
}
}