資料庫安全核心在備份與還原。本文提供 ASP.NET Web 介面,讓管理員選擇資料庫、輸入檔案名、執行 BACKUP DATABASE / RESTORE DATABASE T-SQL 指令,採用參數化防注入,並處理權限與路徑問題。
-- 備份
BACKUP DATABASE @DbName TO DISK = @BackupPath WITH INIT, STATS=10;
-- 還原 (需先設定 SINGLE_USER 踢出連線)
ALTER DATABASE @DbName SET SINGLE_USER WITH ROLLBACK IMMEDIATE;
RESTORE DATABASE @DbName FROM DISK = @BackupPath WITH REPLACE, STATS=10;
ALTER DATABASE @DbName SET MULTI_USER;
<%@ Page Language="C#" AutoEventWireup="true" CodeFile="DatabaseAction.aspx.cs" Inherits="DatabaseAction" %>
<html xmlns="https://www.w3.org/1999/xhtml">
<body>
<form id="form1" runat="server">
<table border="0" width="100%">
<tr><td colspan="2">資料庫備份與還原</td></tr>
<tr><td>選擇資料庫</td><td>
<asp:DropDownList ID="ddlDatabaseList" runat="server" />
</td></tr>
<tr><td>備份檔名</td><td>
<asp:TextBox ID="txtDbFileName" runat="server" />
</td></tr>
<tr><td>操作</td><td>
<asp:RadioButton ID="rbBackup" runat="server" GroupName="action" Text="備份" Checked="true" />
<asp:RadioButton ID="rbRestore" runat="server" GroupName="action" Text="還原" />
</td></tr>
<tr><td></td><td>
<asp:Button ID="btnOK" runat="server" Text="執行" OnClick="btnOK_Click" />
</td></tr>
</table>
</form>
</body>
</html>
public partial class DatabaseAction : Page
{
protected void Page_Load(object sender, EventArgs e)
{
if (!IsPostBack)
{
LoadDatabases();
}
}
private void LoadDatabases()
{
using (var conn = new SqlConnection(GetMasterConnStr()))
using (var cmd = new SqlCommand("SELECT name FROM sys.databases WHERE database_id > 4 ORDER BY name", conn))
{
conn.Open();
ddlDatabaseList.DataSource = cmd.ExecuteReader();
ddlDatabaseList.DataTextField = "name";
ddlDatabaseList.DataValueField = "name";
ddlDatabaseList.DataBind();
}
}
private string GetMasterConnStr()
{
// 連接 master 資料庫,需 sysadmin 權限
return ConfigurationManager.ConnectionStrings["MasterDb"].ConnectionString;
}
protected void btnOK_Click(object sender, EventArgs e)
{
string dbName = ddlDatabaseList.SelectedValue;
string fileName = txtDbFileName.Text.Trim();
if (string.IsNullOrEmpty(fileName))
{
ShowError("請輸入檔案名稱");
return;
}
string backupPath = Path.Combine(@"D:\SQLBackup\", fileName + (rbBackup.Checked ? ".bak" : ""));
// 確保目錄存在且 SQL Server 服務帳號有寫入權限
Directory.CreateDirectory(Path.GetDirectoryName(backupPath));
try
{
using (var conn = new SqlConnection(GetMasterConnStr()))
{
conn.Open();
if (rbBackup.Checked)
{
// 備份
string sql = "BACKUP DATABASE @DbName TO DISK = @Path WITH INIT, STATS=10";
using (var cmd = new SqlCommand(sql, conn))
{
cmd.Parameters.AddWithValue("@DbName", dbName);
cmd.Parameters.AddWithValue("@Path", backupPath);
cmd.ExecuteNonQuery();
}
ShowMessage($"備份完成:{backupPath}");
}
else
{
// 還原
if (!File.Exists(backupPath))
{
ShowError("備份檔案不存在");
return;
}
// 踢出連線
ExecNonQuery(conn, $"ALTER DATABASE [{dbName}] SET SINGLE_USER WITH ROLLBACK IMMEDIATE");
// 還原
string sql = "RESTORE DATABASE @DbName FROM DISK = @Path WITH REPLACE, STATS=10";
using (var cmd = new SqlCommand(sql, conn))
{
cmd.Parameters.AddWithValue("@DbName", dbName);
cmd.Parameters.AddWithValue("@Path", backupPath);
cmd.ExecuteNonQuery();
}
// 恢復多用戶
ExecNonQuery(conn, $"ALTER DATABASE [{dbName}] SET MULTI_USER");
ShowMessage("還原完成");
}
}
}
catch (Exception ex)
{
ShowError($"操作失敗:{ex.Message}");
}
}
private void ExecNonQuery(SqlConnection conn, string sql)
{
using (var cmd = new SqlCommand(sql, conn)) cmd.ExecuteNonQuery();
}
private void ShowMessage(string msg) => ClientScript.RegisterStartupScript(GetType(), "msg", $"alert('{msg}');", true);
private void ShowError(string msg) => ClientScript.RegisterStartupScript(GetType(), "err", $"alert('錯誤:{msg}');", true);
}
sysadmin 或 dbcreator + 目標資料庫 db_owner。SINGLE_USER WITH ROLLBACK IMMEDIATE 強制斷開連線,避免「資料庫正在使用中」錯誤。