在现代的软件开发项目中,数据库扮演着至关重要的角色。无论是Web应用、数据收集系统、支付系统还是工业系统,都需要处理和存储大量的数据。最近,在多个项目中使用了SQL Server数据库。许多技术,如ASP.NET MVC、Web API、Entity Framework等,都内置了对这些数据库的支持。甚至在从模板开始一个项目时,通常会创建一个简单的SQL Server演示数据库。在Visual Studio中,创建新的或编辑现有的SQL Server数据库架构也非常简单,SQL Server还包含了内置的管理工作室IDE。
在启动了几个使用SQL Server的自定义软件项目后,遇到了定期备份数据库的需求。为什么需要备份数据库?都面临着硬盘不可靠、勒索软件病毒、软件故障等问题。手头有最新的备份总是比没有更让人安心。为了自动按计划创建备份,在软件解决方案中包含了一个小型项目,其任务就是创建备份副本。
与在主应用程序中启用备份功能相比,拥有一个单独的项目的优势在于,不需要每次都将独特的备份功能插入新项目,并考虑如何使用它。
备份项目非常简单,它基于C#控制台应用程序。该应用程序设计为定期按计划启动。
考虑应用程序代码,它并不复杂:
using System;
using System.Data.SqlClient;
using System.IO;
using System.Threading;
using Ionic.Zip;
namespace SimpleDBBackup
{
class Program
{
// 本地SQL Server连接字符串
static string connectionString = "Server=localhost;Integrated Security=True";
// 可选:使用凭据连接
static string connectionString = "Server=localhost;user id=user2018;password=MYDBPASSWORD;";
// 要备份的数据库名称
static string[] saDatabases = new string[] {
"shop",
"frontend",
"accounting"
};
// 备份目录。请注意:文件比DeletionDays旧将自动删除
static string backupDir = @"C:\DB_Backups";
// 删除比DeletionDays旧的备份。设置为0则永不删除备份
static int DeletionDays = 10;
static Mutex mutex = new Mutex(true, "Global\\SimpleDBBackupMutex");
static void Main(string[] args)
{
// 只允许应用程序的单个实例
if (!mutex.WaitOne(TimeSpan.Zero, true))
{
Console.WriteLine("程序已经在运行!");
return;
}
if (DeletionDays > 0)
DeleteOldBackups();
DateTime dtNow = DateTime.Now;
try
{
using (SqlConnection sqlConnection = new SqlConnection(connectionString))
{
sqlConnection.Open();
foreach (string dbName in saDatabases)
{
string backupFileNameWithoutExt = String.Format("{0}\\{1}_{2:yyyy-MM-dd_hh-mm-ss-tt}", backupDir, dbName, dtNow);
string backupFileNameWithExt = String.Format("{0}.bak", backupFileNameWithoutExt);
string zipFileName = String.Format("{0}.zip", backupFileNameWithoutExt);
string cmdText = string.Format("BACKUP DATABASE {0}\r\nTO DISK = '{1}'", dbName, backupFileNameWithExt);
using (SqlCommand sqlCommand = new SqlCommand(cmdText, sqlConnection))
{
sqlCommand.CommandTimeout = 0;
sqlCommand.ExecuteNonQuery();
}
using (ZipFile zip = new ZipFile())
{
zip.CompressionLevel = Ionic.Zlib.CompressionLevel.BestCompression;
zip.AddFile(backupFileNameWithExt);
zip.Save(zipFileName);
}
File.Delete(backupFileNameWithExt);
}
sqlConnection.Close();
}
}
catch (Exception ex)
{
Console.WriteLine(ex.ToString());
}
mutex.ReleaseMutex();
}
static void DeleteOldBackups()
{
try
{
string[] files = Directory.GetFiles(backupDir);
foreach (string file in files)
{
FileInfo fi = new FileInfo(file);
if (fi.CreationTime < DateTime.Now.AddDays(-DeletionDays))
fi.Delete();
}
}
catch (Exception ex)
{
Console.WriteLine(ex.ToString());
}
}
}
}
应用程序由几个主要块组成:
让更详细地考虑这些块:
在这里,指定数据库的连接字符串。在最简单的情况下,如果使用具有默认设置的本地服务器,第一行就足够了。如果想使用登录名和密码连接到服务器,请取消注释并修改第二行。
在主函数的开始,安装了Mutex(互斥)对象。这个对象只能以单个实例存在。在这个程序中,使用Mutex来防止程序第二次启动。使用Mutex对象,可以为进程和线程之间的交互做许多有趣和迷人的事情。
主函数中的下一个有趣的块是连接到数据库并执行备份命令:
这里一切都很简单:获取具有创建日期属性的文件列表,并删除所有创建日期早于当前日期减去指定天数的文件。
在程序配置并编译为发布版本后,需要将其与库一起复制到单独的目录中,例如C:\SimpleDbBackup,同时为数据库创建一个目录(在这个例子中是C:\DB_Backups)。
现在,需要使用Windows任务计划程序创建一个定期任务,以便它每天或每天两次按计划运行,根据喜好。建议现在手动创建任务。也许将来会在主程序的代码中添加创建计划任务的功能,但目前,建议手动执行。