私房小工具 - 通用型 MSDTC 測試 ASPX
0 |
先前介紹過用單一 .aspx 檔在線上環境測試 MSDTC 是否正常的小技巧,今天使用時發現一個小缺點:當有多台 SQL 需要驗證分散式交易功能,因為連線字串寫死在程式裡,需反覆修改程式儲存重測,有點麻煩。
順手把 DTC 測試程式改寫成通用版,將連線字串裡的 Data Source (SQL IP)、User Id、Password 改為人工輸入,如此,部署一次程式即可測完多台 SQL。
若測試成功將顯示測試時間、兩次 GetDate() 內容、 Transaction.Current.TransactionInformation 的 LocalIdentifier 及 DistributedIdentifier,DistributedIdentifier 為非空白 GUID 表示有成功啟用分散式交易。
失敗時則會顯示紅字錯誤訊息:
既然是單檔 .aspx,網頁,我直接用 ASP.NET WebForm TextBox、Button + Server Event 簡化程式碼,只花了一百行就搞定,WebForm 在某些應用場合還是很有優勢。
最後,評估安全性是一定要的:測試查詢寫死 SELECT GETDATE() AS D,不致有被注入 SQL 指令的風險,唯一風險來自被用來猜測帳號密碼(例如:試連限定該網站 IP 才能存取的 SQL 主機),為降低風險,我再多加了一層逾期鎖定,在程式碼寫死有效期限,逾時即無法使用。即使忘記刪除遺留在伺服器上,連線功能也會自動失效避免被拿來做壞事。當然,最嚴謹的做法還是每次測試完就從伺服器移除。
以下是完整程式碼,提供有類似需要的朋友參考:
<%@ Page Language="C#" %>
<%@ Import Namespace="System.Data.SqlClient" %>
<%@ Import Namespace="System.Transactions" %>
<script runat="server">
const string expireTime = "201911161200";
protected void btnTest_Click(object sender, EventArgs e)
{
try
{
if (DateTime.Now.ToString("yyyyMMddHHmm").CompareTo(expireTime) > 0)
throw new Exception("Tool Expired");
TestDtc(txtDataSource.Text, txtUserId.Text, txtPassword.Text);
}
catch (Exception ex)
{
preDisplay.Attributes["class"] = "err";
preDisplay.InnerHtml = "**ERROR**\n" + ex.ToString();
}
}
void TestDtc(string dataSrc, string uid, string pwd)
{
Action<string, string> validate =
(v, n) => { if (string.IsNullOrEmpty(v)) throw new ArgumentException(n + " is null or empty."); };
validate(dataSrc, "Data Source");
validate(uid, "User Id");
validate(pwd, "Password");
var scsb = new SqlConnectionStringBuilder();
scsb.DataSource = dataSrc;
scsb.UserID = uid;
scsb.Password = pwd;
TestDtc(scsb.ConnectionString);
}
void TestDtc(string cnStr)
{
using (var tx = new TransactionScope())
{
var css = "normal";
var sb = new StringBuilder();
sb.AppendFormat("Test DTC at {0:HH:mm:ss.fff}\n", DateTime.Now);
sb.AppendLine(querySqlServer(cnStr));
sb.AppendLine(querySqlServer(cnStr));
var txInfo = Transaction.Current.TransactionInformation;
sb.AppendLine("Local Id = " + txInfo.LocalIdentifier);
sb.AppendLine("Distributed Id = " + txInfo.DistributedIdentifier);
if (txInfo.DistributedIdentifier != Guid.Empty)
{
sb.AppendLine("*** TEST PASSED ****");
css = "pass";
}
preDisplay.Attributes["class"] = css;
preDisplay.InnerHtml = sb.ToString();
tx.Complete();
}
}
private string querySqlServer(string cnStr)
{
cnStr = cnStr.TrimEnd(';') + ";Application Name=" + Guid.NewGuid().ToString();
using (SqlConnection cn = new SqlConnection(cnStr))
{
SqlCommand cmd = new SqlCommand("SELECT getdate() as D", cn);
cn.Open();
SqlDataReader dr = cmd.ExecuteReader();
dr.Read();
var res = "GetDate = " + dr["D"];
cn.Close();
return res;
}
}
</script>
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>DTC Test</title>
<style>
pre { background-color: #eee; padding: 6px; }
pre.pass { background-color: #c9e488; }
pre.err { color: brown; }
</style>
</head>
<body>
<form runat="server">
<table>
<tr>
<td>Data Source</td>
<td><asp:TextBox runat="server" ID="txtDataSource"></asp:TextBox></td>
</tr>
<tr>
<td>User Id</td>
<td><asp:TextBox runat="server" ID="txtUserId"></asp:TextBox></td>
</tr>
<tr>
<td>Password</td>
<td><asp:TextBox runat="server" ID="txtPassword" TextMode="Password"></asp:TextBox></td>
</tr>
</table>
<asp:Button ID="btnTest" Text="Test DTC" runat="server" OnClick="btnTest_Click" />
<pre runat="server" id="preDisplay"></pre>
</form>
</body>
</html>
A simple universal WebForm .aspx for testing distributed transaction to SQL servers.
Comments
Be the first to post a comment