在C#批量还原备份MySql--cmd命令操作篇 我们了解了如何使用mysqldump.exe,mysql.exe命令来备份还原mysql,在这篇文章我们学习有C#来操作,并制作一个备份还原工具。
我们要操作cmd.exe使用到了Process类,使用这个类首先要引入命名空间 System.Diagnostics,此类提供对本地和远程进程的访问并能够启动和停止本地系统进程。
/// <summary>
/// 执行Cmd命令
/// </summary>
/// <param name="workingDirectory">要启动的进程的目录</param>
/// <param name="command">要执行的命令</param>
public static void StartCmd(String workingDirectory, String command)
{
Process p = new Process();
p.StartInfo.FileName = "cmd.exe";
p.StartInfo.WorkingDirectory = workingDirectory;
p.StartInfo.UseShellExecute = false;
p.StartInfo.RedirectStandardInput = true;
p.StartInfo.RedirectStandardOutput = true;
p.StartInfo.RedirectStandardError = true;
p.StartInfo.CreateNoWindow = true;
p.Start();
p.StandardInput.WriteLine(command);
p.StandardInput.WriteLine("exit");
p.WaitForExit();
p.Close();
}
在看看如何使用上方法:
string appDircectroy="C:\\Program Files\\MySQL\\MySQL Server 5.5\\bin"StartCmd(appDircectroy,cmd);
string cmd="mysqldump -hlocalhost -uroot -proot --default-character-set=utf8 --lock-tables --routines --force --quick Dbname>d:\backup.sql"
这里我们同Process操作cmd.exe,调用mysqldump.exe 执行命令"-hlocalhost -uroot -proot --default-character-set=utf8 --lock-tables --routines --force --quick Dbname>d:\backup.sql"备份数据库。其实我们也可以直接通过Process类直接调用mysqldump.exe进程执行命令,写法如下:
/// <summary>
/// 使用mysqldump执行command命令
/// </summary>
/// <param name="AppPath">mysqldump.exe的目录</param>
/// <param name="command"></param>
public static void StartMySqldump(string AppPath, string command)
{
ProcessStartInfo psi = new ProcessStartInfo(AppPath + @"\mysqldump.exe");
psi.Arguments = command;
psi.UseShellExecute = false; psi.RedirectStandardOutput = true;
psi.RedirectStandardInput = true;
psi.RedirectStandardError = true;
psi.CreateNoWindow = true;
Process pro = Process.Start(psi);
pro.WaitForExit();
pro.Close();
}
调用的方法和上面的使用基本一致,就是command命令不需要写mysqldump 改为"-hlocalhost -uroot -proot --default-character-set=utf8 --lock-tables --routines --force --quick Dbname>d:\backup.sql" 。
我再这里使用方法1,比较灵活方便,因为我们原因的使用也可以用到此方法执行mysql.exe还原命令,上篇中说到还原时,数据库如何不存在的使用,我们要先创建数据库,再还原,那么就是要执行两天命令,我们可以改一下方法1,使它可以执行多条命令,如下:
/// <summary>
/// 执行Cmd命令
/// </summary>
/// <param name="workingDirectory">要启动的进程的目录</param>
/// <param name="command">要执行的命令</param>
public static void StartCmd(String workingDirectory, String[] commands)
{
Process p = new Process();
p.StartInfo.FileName = "cmd.exe";
p.StartInfo.WorkingDirectory = workingDirectory;
p.StartInfo.UseShellExecute = false;
p.StartInfo.RedirectStandardInput = true;
p.StartInfo.RedirectStandardOutput = true;
p.StartInfo.RedirectStandardError = true;
p.StartInfo.CreateNoWindow = true;
p.Start();
foreach (string cmd in commands)
{
p.StandardInput.WriteLine(cmd);
}
p.StandardInput.WriteLine("exit");
p.WaitForExit();
p.Close();
}
有了上面的函数,编写一个winform备份还原工具就比较容易实现了,但是现在我要备份300多个数据库,我要生成300多个数据库备份文件,当然我们也可以通过命令将300多个数据库,即所有数据库(包含mysql自带的系统库)备份到一个文件夹。还有一点要注意就是用户体验,假如备份的一个数据库很大,那么直接调用就会吧UI卡死,所以我们要用异步线程来解决此问题,我打算用四个线程同时执行备份,直到300多个数据库执行完毕。
有人可能对线程不熟悉,不用怕,其实.net提供了一个异步线程的封装 BackgroundWorker,用此类可以很简单实现。 我们用到BackgroundWorker提供的两个事件DoWork和RunWorkerCompleted,DoWork中写线程开始要执行的任务,RunWorkerCompleted中写线程执行的任务结束要执行的工作。看看简单的代码示例:
BackgroundWorker backgroundWorker = new BackgroundWorker();
backgroundWorker.DoWork += new DoWorkEventHandler(backgroundWorker_DoWork);
backgroundWorker.RunWorkerCompleted += new RunWorkerCompletedEventHandler(backgroundWorker_RunWorkerCompleted);
backgroundWorker.RunWorkerAsync(remainDbNames); //remainDbName是list<string>类型, 传入的参数,我保存要备份的库的名字,备份一个库就移除一个库
private void backgroundWorker_DoWork(object sender, DoWorkEventArgs e)
{
List<string> names = e.Argument as List<string>; //获取传入的参数ramainDbName
BackupMany(filePath, names, boolBackdata); //执行备份方法
//e.Result = e.Argument.ToString(); //完成任务时向RunWorkerCompleted方法传递的结果参数
}
private void backgroundWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
// writeText(textResult,true,"完成数据库"+e.Result.ToString()+"备份操作" + Environment.NewLine);
}
使用就是那么简单,下面看看我启用四个线程同时备份:
//只有在备份按钮事件中生成个异步线程即可
for (int i = 0; i < 4; i++)
{
BackgroundWorker backgroundWorker = new BackgroundWorker();
backgroundWorker.DoWork += new DoWorkEventHandler(backgroundWorker_DoWork);
backgroundWorker.RunWorkerCompleted += new RunWorkerCompletedEventHandler(backgroundWorker_RunWorkerCompleted);
backgroundWorker.RunWorkerAsync(remainDbNames);
}
下面看看单个线程具体如何从remainDbName中取数据库的名字执行备份
object lockobject = new object(); //备份时的lock对象
delegate void WriteText(TextBox textbox, bool append, string text);
private void writeText(TextBox textbox, bool append, string msg)
{
if (this.InvokeRequired)
{
WriteText d = new WriteText(writeText);
object[] obj = new object[3];
obj[0] = textbox;
obj[1] = append;
obj[2] = msg;
this.Invoke(d, obj);
}
else
{
if (append)
textbox.AppendText(msg);
else
textbox.Text = msg;
}
}
private void BackupMany(string path, List<string> dbNames, bool backupdata)
{
string name = null;
lock (lockobject)
{
if (dbNames.Count > 0)
{
name = dbNames[0];
dbNames.RemoveAt(0);
}
}
if (name != null)
{
Backup(path, name, backupdata);
BackupMany(path, dbNames, backupdata);
}
else
{
timer1.Enabled = false;
writeText(textResult, true, "全部执行完成!");
}
}
private void Backup(string path, string databaseName, bool backupdata)
{
try
{
writeText(textResult, true, DateTime.Now.ToString() + " 开始数据库" + databaseName + "备份" + Environment.NewLine);
//String command = "mysqldump --quick --host=localhost --default-character-set=gb2312 --lock-tables --verbose --force --port=端口号 --user=用户名 --password=密码 数据库名 -r 备份到的地址";
//构建执行的命令
String directory = path + "\\" + databaseName + ".sql";
String command;
if (backupdata)
{
command = string.Format("mysqldump --quick --host={1} --default-character-set={2} --lock-tables --routines --force --port={3} --user={4} --password={5} {6} -r \"{0}\"",
directory, host, characterSet, port, user, password, databaseName);
}
else
{
command = string.Format("mysqldump --quick --host={1} --default-character-set={2} -d --lock-tables --routines --force --port={3} --user={4} --password={5} {6} -r \"{0}\"",
directory, host, characterSet, port, user, password, databaseName);
}
Cmd.StartCmd(appDirecroty, command);
writeText(textResult, true, DateTime.Now.ToString() + @" 数据库已成功备份到 " + directory + " 文件中" + Environment.NewLine);
counter++;
writeText(textBox1, false, "共" + totlecount + "个文件,已完成" + counter + "个文件");
}
catch (Exception ex)
{
writeText(txtErr, true, DateTime.Now.ToString() + " 数据库" + databaseName + "备份失败!");
}
}
writeText是向TextBox写入文本的方法,因为副线程操作主线程生成的控件时,会有线程安全问题,所以要用异步委托。如何你知道这个怎么用,其实还有一个简单点的方法,就是把前程安全检查给关闭掉,在窗体onload中可以加入Control.CheckForIllegalCrossThreadCalls = false; 这句就可以跨线程操作UI而不会报错。
还原的实现和备份的操作差不多,这里就不提了。本想上传个一下代码可是找不到上传的方法。
刚刚学会怎么上传,这里是连接 源代码