在不阻塞UI的情况下在新任务中生成文件并处理异常

本文关键字:文件 异常 处理 新任务 UI 情况下 | 更新日期: 2023-09-27 18:07:04

我试图构建。. NET 4.0应用程序,将生成水晶报表文件。我有一个工作版本,但一切都是同步工作-在我点击生成按钮后,应用程序冻结了5秒。
相反,我想显示进度指示器,它会说文件正在生成,但我的代码有问题。

生成报告的方法如下所示:

public static Task<string> GenerateLetter()
{
    const string destinationLocation = @"C:'Export";
    const string source = @"C:'Test_Report.rpt";
     return Task<string>.Factory.StartNew(() =>
    {
        if (File.Exists(source))
        {
            var crReportDocument = new ReportDocument();
            crReportDocument.Load(source);
            var destinationFolder = new DirectoryInfo(destinationLocation);
            if (!destinationFolder.Exists)
                destinationFolder.Create();
            var timeStamp = DateTime.Now.ToString().Replace(":", "").Replace(" ", "").Replace("-", "");
            var destination = Path.Combine(destinationFolder.FullName, timeStamp + ".pdf");
            var crDiskFileDestinationOptions = new DiskFileDestinationOptions { DiskFileName = destination };
            var crExportOptions = crReportDocument.ExportOptions;
            {
                crExportOptions.DestinationOptions = crDiskFileDestinationOptions;
                crExportOptions.ExportDestinationType = ExportDestinationType.DiskFile;
                crExportOptions.ExportFormatType = ExportFormatType.PortableDocFormat;
            }
            try
            {
                crReportDocument.Export();
                return destination;
            }
            catch (Exception ex)
            {
                throw new SystemException("Error exporting!", ex);
            }
        }
        throw new FileNotFoundException("Report file not found!", source);
    });
}

方法返回生成文件的本地化,否则如果出现错误将抛出异常。

在我的表单中,我放置了一个按钮和一个选框进度条。我把这个处理程序附加到按钮的点击:

private void button1_Click(object sender, EventArgs e)
{
    progressBar1.Visible = true;
    try
    {
        Task<string> xx = ReportGenerator.GenerateLetter();
        MessageBox.Show(xx.Result);
        progressBar1.Visible = false;
    }
    catch (AggregateException ae)
    {
        ae.Handle(x =>
        {
            if (x is FileNotFoundException)
            {
                var ex = x as FileNotFoundException;
                MessageBox.Show(ex.Message,"File not found");
                progressBar1.Visible = false;
            }
            else if (x is SystemException)
            {
                var ex = x as SystemException;
                MessageBox.Show(ex.Message,"Other exception");
                progressBar1.Visible = false;
            }
            return true;
        });
    }
}

我的问题:

  1. 我该如何解决这个问题?所以当我点击按钮后,UI不会冻结。

  2. 我可以在。net 4.0中做到这一点吗?还是必须使用4.5
  3. 我可以隐藏进度条总是在任务完成后(成功或例外)?在jQuery中,我可以使用deferred。c#中有这样的东西吗?

在不阻塞UI的情况下在新任务中生成文件并处理异常

任务是很好的起点。

你可以在4.0和4.5 .net框架中使用它们。

为了确保你的TPL任务在线程上运行,与UI线程不同,你应该使用TaskScheduler。默认值(例如,看看这个线程)。

所以你应该这样做:

Task<string>.Factory.StartNew(() =>
{
    // Your logic here
}, CancellationToken.None, TaskCreationOptions.None, TaskScheduler.Default)
.ContinueWith((p) =>
{
     // This will be UI thread
     // p is the parent task
      progressBar1.Visible = false;
     // if parent task has faulted
     if (p.IsFaulted)
     {
          // Do with p.Exception field what ever you want - log it, show it.
          // for .net 4.0 you must read this property in order to prevent
          // application failure
          MessageBox.Show(p.Exception.Message);
          return;
     }
     // Here you know all about the parent task, so you can do the logic you want:
     MessageBox.Show(p.Result);
}, TaskScheduler.FromCurrentSynchronizationContext());

延续任务将在UI线程上执行,因为TaskScheduler.FromCurrentSynchronizationContext()参数。

在。net 4.0中,你还必须处理来自延续任务的异常。您可以使用try-catch块来实现,例如

Task.Result是阻塞调用,所以你的UI线程冻结,直到任务完成。解决这个问题的方法是使用新的async/await特性。你甚至可以在。net 4.0中使用它,但是你需要在。net 4.0中包含异步目标包。

将事件处理程序方法设置为async并"await"任务。

 private async void button1_Click(object sender, EventArgs e)
 {
    progressBar1.Visible = true;
    try
    {
       string result =await ReportGenerator.GenerateLetter();
       MessageBox.Show(result);
       progressBar1.Visible = false;
    }
    catch{
    ........
 }