信息系统是数据密集型的,数据的套帐,备份,还原是客户最希望有的功能,这一节课就讨论下C/S系统下数据库的异步备份和还原,B/S系统的数据备份和还原和这个类似。
既然是异步,首先会想到使用多线程技术。.NET平台提供了一整套的线程处理技术,使用线程的好处是,可以让一个线程做一件事情,多个线程之间根据时间片机制抢夺CPU和I/O资源,UI线程用于绘制界面,保证界面永远对客户的响应,而工作线程用于计算工作。
除了从头开发线程外,.NET也提供了一个封装好的线程组件BackgroundWorker,该组件让您能够在应用程序的主要 UI 线程以外的其他线程上异步(“在后台”)执行耗时的操作。比如耗时耗资源的常用操作如下:
- 图像下载
- Web 服务调用
- 文件下载和上载(包括点对点应用程序)
- 复杂的本地计算
- 数据库事务
- 本地磁盘访问(相对于内存访问来说其速度很慢)
类似这样的操作可能导致用户界面在操作运行时挂起。如果需要用户界面的响应却遇到与此类操作关联的长时间延迟,BackgroundWorker组件可以提供一种方便的解决方案。若要使用 BackgroundWorker,只需要告诉该组件要在后台执行的耗时的辅助方法,然后调用 RunWorkerAsync 方法。在辅助方法以异步方式运行的同时,您的调用线程继续正常运行。该方法运行完毕,BackgroundWorker激发 RunWorkerCompleted 事件(可选择包含操作结果)向调用线程发出警报。
“组件”选项卡的“工具箱”中提供了 BackgroundWorker组件。VS2005,VS2008, VS2010都有这个组件,如下图:
若要向窗体添加 BackgroundWorker,请将 BackgroundWorker组件拖到窗体上即可。
若要启动异步操作,请使用 RunWorkerAsync 方法。RunWorkerAsync 采用一个可选的 object 参数,可以使用该参数将变量传递给辅助方法。BackgroundWorker类公开 DoWork 事件,您的辅助线程通过 DoWork 事件处理程序附加到该事件。
BackgroundWorker包含三个主要的事件:
1,DoWork 事件
调用 RunWorkerAsync 方法时将引发此事件。在此,您就可以启动操作来执行可能很耗时的工作。
2, RunWorkerCompleted 事件
DoWork事件处理程序返回时将引发此事件。如果操作成功完成,并且其结果在 DoWork事件处理程序中进行了分配,则可以通过RunWorkerCompletedEventArgs.Result属性访问该结果。
3, ProgressChanged 事件
调用 ReportProgress 方法时将引发此事件。ProgressChanged事件向用户报告异步操作的进度
熟悉了组建用法后,开始做异步的数据库备份和还原
一,异步数据库备份,界面如下:
点击开始备份按钮,选择一个文件夹保存备份文件
然后开始备份,线程会不停的汇报它自己执行的进度,用一个进度条来接受线程的进度,这个是通过ProgressChanged 事件完成的。
当备份完成后,线程会通知系统,它已经返回。如果中间线程出现执行错误,通过程序来捕获。这个是通过RunWorkerCompleted 事件来完成的,而备份任务是通过DoWork 事件来完成的。
异步备份数据库的代码如下:
- //启动备份
- private void btnBackUp_Click(object sender, EventArgs e)
- {
- try
- {
- SaveFileDialog sfd = new SaveFileDialog();
- sfd.Title = "请选择备份保存的目录";
- sfd.Filter = "(*.Bak)|*.Bak;|(All Files)|*.*";
- if (sfd.ShowDialog() == DialogResult.OK && sfd.FileName != string.Empty)
- {
- string filePath = sfd.FileName;
- //启动异步备份,开始执行DoWork事件
- this.backgroundWorker1.RunWorkerAsync(new string[] { filePath, "InstallationsManager" });
- }
- }
- catch (System.Exception ex)
- {
- MessageBox.Show("异常:" + ex.Message, "提示", MessageBoxButtons.OK, MessageBoxIcon.Error);
- }
- }
- //备份数据库
- private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
- {
- try
- {
- using (SqlConnection con = new SqlConnection(ConfigurationManager.ConnectionStrings["LongXiConnectionString"].ConnectionString))
- {
- con.Open();
- //汇报进度
- this.backgroundWorker1.ReportProgress(20);
- string[] args = (string[])e.Argument;
- string filePath = args[0];
- SqlCommand cmd = new SqlCommand();
- cmd.Connection = con;
- //备份数据库
- cmd.CommandText = "use master;backup database " + args[1] + " to disk = '" + filePath + "' ";
- cmd.ExecuteNonQuery();
- this.backgroundWorker1.ReportProgress(60);
- //线程完成时返回的信息
- e.Result = "备份数据成功!";
- //this.label5.Text = "aaaaaaaa"; //Control.CheckForIllegalCrossThreadCalls = false
- this.backgroundWorker1.ReportProgress(100);
- }
- }
- catch (System.Exception ex)
- {
- MessageBox.Show("异常:" + ex.Message, "提示", MessageBoxButtons.OK, MessageBoxIcon.Error);
- }
- }
- //线程执行完成后的收尾工作
- private void backgroundWorker1_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
- {
- try
- {
- if (e.Error != null)
- {
- MessageBox.Show("异常:" + e.Error.Message, "提示", MessageBoxButtons.OK, MessageBoxIcon.Error);
- }
- else if (e.Cancelled)
- {
- MessageBox.Show("线程已经退出!", "提示", MessageBoxButtons.OK, MessageBoxIcon.Information);
- }
- else
- {
- //MessageBox.Show(e.Result.ToString(), "提示", MessageBoxButtons.OK, MessageBoxIcon.Information);
- //向UI提示备份数据库成功
- this.lblInfo.Text = e.Result.ToString();
- }
- }
- catch (System.Exception ex)
- {
- MessageBox.Show("异常:" + ex.Message, "提示", MessageBoxButtons.OK, MessageBoxIcon.Error);
- }
- }
- //向UI线程汇报进度,使进度条的值增加
- private void backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e)
- {
- try
- {
- this.pbarBackUp.Value = e.ProgressPercentage;
- }
- catch (System.Exception ex)
- {
- MessageBox.Show("异常:" + ex.Message, "提示", MessageBoxButtons.OK, MessageBoxIcon.Error);
- }
- }
二,异步还原数据库
和备份类似,这里不再讲解,代码如下:
- //启动还原
- private void btnRestore_Click(object sender, EventArgs e)
- {
- //第一次后悔药
- if (MessageBox.Show("该操作将数据覆盖!如果选择[是],将原来的数据覆盖;如果选择[否],将取消恢复.", "提示", MessageBoxButtons.YesNo, MessageBoxIcon.Question) == DialogResult.No)
- return;
- //第二次后悔药
- if (MessageBox.Show("请再次确认,该操作不能恢复!如果选择[是],将原来的数据覆盖;如果选择[否],将取消恢复.", "提示", MessageBoxButtons.YesNo, MessageBoxIcon.Warning) == DialogResult.No)
- return;
- //没得后悔药吃了,开始执行还原
- try
- {
- OpenFileDialog ofd = new OpenFileDialog();
- ofd.Title = "请选择要恢复的备份文件";
- ofd.Filter = "(*.Bak)|*.Bak;|(All Files)|*.*";
- if (ofd.ShowDialog() == DialogResult.OK && ofd.FileName != string.Empty)
- {
- string filePath = ofd.FileName;
- //启动异步还原,开始执行DoWork事件
- this.backgroundWorker1.RunWorkerAsync(new string[] { filePath, "InstallationsManager" });
- }
- }
- catch (System.Exception ex)
- {
- MessageBox.Show("异常:" + ex.Message, "提示", MessageBoxButtons.OK, MessageBoxIcon.Error);
- }
- }
- //还原数据库
- private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
- {
- this.backgroundWorker1.ReportProgress(30);
- using (SqlConnection con = new SqlConnection(ConfigurationManager.ConnectionStrings["LongXiConnectionString"].ConnectionString))
- {
- con.Open();
- //汇报进度
- this.backgroundWorker1.ReportProgress(20);
- Thread.Sleep(1000);
- string[] args = (string[])e.Argument;
- string filePath = args[0];
- SqlCommand cmd = new SqlCommand();
- this.backgroundWorker1.ReportProgress(30);
- Thread.Sleep(1000);
- this.backgroundWorker1.ReportProgress(60);
- cmd.Connection = con;
- //还原数据库
- cmd.CommandText = "use master;alter database " + args[1] + " set offline with rollback immediate;restore database " + args[1] + " from disk = '" + filePath + "' with replace";
- cmd.ExecuteNonQuery();
- this.backgroundWorker1.ReportProgress(80);
- Thread.Sleep(1000);
- this.backgroundWorker1.ReportProgress(100);
- //线程完成时返回的信息
- e.Result = "恢复数据成功!";
- }
- }
- //汇报进度,使进度条的值增加
- private void backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e)
- {
- this.pbarRestore.Value = e.ProgressPercentage;
- }
- //线程执行完成后的收尾工作
- private void backgroundWorker1_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
- {
- if (e.Error != null)
- {
- MessageBox.Show("异常:" + e.Error.Message, "提示", MessageBoxButtons.OK, MessageBoxIcon.Error);
- }
- else if (e.Cancelled)
- {
- MessageBox.Show("线程已经退出!", "提示", MessageBoxButtons.OK, MessageBoxIcon.Information);
- }
- else
- {
- //MessageBox.Show(e.Result.ToString(), "提示", MessageBoxButtons.OK, MessageBoxIcon.Information);
- //向UI提示恢复数据库成功
- this.lblInfo.Text = e.Result.ToString();
- }
- }