C# 在新任务中生成文件而不阻塞UI并处理异常

C# 在新任务中生成文件而不阻塞UI并处理异常,c#,.net-4.0,crystal-reports,task-parallel-library,task,C#,.net 4.0,Crystal Reports,Task Parallel Library,Task,我正在尝试构建生成Crystal Reports文件的.NET4.0应用程序。 我有一个工作版本,但一切都是同步工作的-在我单击“生成”按钮后,应用程序将冻结5秒钟。 相反,我想显示一个进度指示器,表明文件正在生成,但我的代码有问题 生成报告的方法如下所示: public static Task<string> GenerateLetter() { const string destinationLocation = @"C:\Export"; const strin

我正在尝试构建生成Crystal Reports文件的.NET4.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);
    });
}
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());
公共静态任务生成器()
{
常量字符串destinationLocation=@“C:\Export”;
常量字符串源=@“C:\Test_Report.rpt”;
返回Task.Factory.StartNew(()=>
{
if(File.Exists(source))
{
var crReportDocument=新报告文档();
crReportDocument.Load(源);
var destinationFolder=新目录信息(destinationLocation);
如果(!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;
}
尝试
{
crReportDocument.Export();
返回目的地;
}
捕获(例外情况除外)
{
抛出新的系统异常(“导出错误!”,ex);
}
}
抛出新的FileNotFoundException(“未找到报告文件!”,源);
});
}
方法返回生成文件的本地化,或者在出现问题时抛出异常

在我的表格中,我放置了一个按钮和一个字幕进度条。我已将此处理程序附加到按钮的单击:

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;
        });
    }
}
private void按钮1\u单击(对象发送者,事件参数e)
{
progressBar1.Visible=true;
尝试
{
任务xx=ReportGenerator.GenerateLetter();
MessageBox.Show(xx.Result);
progressBar1.Visible=false;
}
捕获(聚合异常ae)
{
ae.Handle(x=>
{
如果(x是FileNotFoundException)
{
var ex=x作为FileNotFoundException;
Show(例如Message,“未找到文件”);
progressBar1.Visible=false;
}
else if(x是系统异常)
{
var ex=x作为系统异常;
MessageBox.Show(例如Message,“其他异常”);
progressBar1.Visible=false;
}
返回true;
});
}
}
我的问题是:

  • 我怎样才能解决这个问题?因此,单击按钮后,UI不会冻结。
  • 我可以用.NET 4.0来实现这一点,还是必须用4.5来实现这一点
  • 我是否可以在任务完成后始终隐藏progressbar(成功,但有例外)?在jQuery中,我可以使用deffered.always,C#中是否有类似的内容

  • Task.Result
    正在阻止调用,因此您的UI线程将冻结,直到任务完成。 解决此问题的方法是使用新的
    异步
    /
    等待
    功能。即使在.NET4.0中也可以使用它,但需要包括

    使事件处理程序方法异步并“等待”任务

     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{
        ........
     }
    

    Task.Result
    正在阻止调用,因此您的UI线程将冻结,直到任务完成。 解决此问题的方法是使用新的
    异步
    /
    等待
    功能。即使在.NET4.0中也可以使用它,但需要包括

    使事件处理程序方法异步并“等待”任务

     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{
        ........
     }
    
    这是一个很好的开始

    您可以在4.0和4.5.NETFramework中使用它们

    为了确保TPL任务在线程上运行,与UI线程不同,您应该使用(例如,查看)

    所以你应该这样做:

    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);
        });
    }
    
    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());
    
    Task.Factory.StartNew(()=>
    {
    //你的逻辑在这里
    },CancellationToken.None,TaskCreationOptions.None,TaskScheduler.Default)
    .ContinueWith((p)=>
    {
    //这将是UI线程
    //p是父任务
    progressBar1.Visible=false;
    //如果父任务出现故障
    如果(p.IsFaulted)
    {
    //对p.Exception字段执行任何您想要的操作-记录它,显示它。
    //对于.net 4.0,必须读取此属性才能防止
    //应用程序失败
    MessageBox.Show(p.Exception.Message);
    返回;
    }
    //在这里,您了解有关父任务的所有信息,因此可以执行所需的逻辑:
    MessageBox.Show(p.Result);
    },TaskScheduler.FromCurrentSynchronizationContext());
    
    由于此参数,将在UI线程上执行延续任务

    在.NET4.0中,您还必须处理延续任务中的异常。例如,您可以使用try-catch-block来执行此操作。

    是一个很好的起点

    您可以在4.0和4.5.NETFramework中使用它们

    为了确保TPL任务在线程上运行,与UI线程不同,您应该使用(例如,查看)

    所以你应该这样做:

    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);
        });
    }
    
    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());
    
    Task.Factory.StartNew(()=>
    {
    //你的逻辑在这里
    },CancellationToken.None,TaskCreationOptions.None,TaskScheduler.Default)
    .ContinueWith((p)=>
    {
    //这将是UI线程
    //p是父任务
    progressBar1.Visible=false;
    //如果父任务出现故障
    如果(p.IsFaulted)
    {
    //对p.Exception字段执行任何您想要的操作-记录它,显示它。
    //对于.net 4.0,必须在中读取此属性